diff --git a/jedi/api.py b/jedi/api.py index 8b4ebec5..6092e10d 100644 --- a/jedi/api.py +++ b/jedi/api.py @@ -700,3 +700,83 @@ def set_debug_function(func_cb=debug.print_to_stdout, warnings=True, debug.enable_warning = warnings debug.enable_notice = notices debug.enable_speed = speed + + +# TODO move to a better place. +def usages(definitions, search_name, mods): + def compare_array(definitions): + """ `definitions` are being compared by module/start_pos, because + sometimes the id's of the objects change (e.g. executions). + """ + result = [] + for d in definitions: + module = d.get_parent_until() + result.append((module, d.start_pos)) + return result + + def check_call(call): + result = [] + follow = [] # There might be multiple search_name's in one call_path + call_path = list(call.generate_call_path()) + for i, name in enumerate(call_path): + # name is `pr.NamePart`. + if name == search_name: + follow.append(call_path[:i + 1]) + + for f in follow: + follow_res, search = evaluate.goto(call.parent, f) + follow_res = usages_add_import_modules(follow_res, search) + + compare_follow_res = compare_array(follow_res) + # compare to see if they match + if any(r in compare_definitions for r in compare_follow_res): + scope = call.parent + result.append(api_classes.Usage(search, scope)) + + return result + + if not definitions: + return set() + + compare_definitions = compare_array(definitions) + mods |= set([d.get_parent_until() for d in definitions]) + names = [] + for m in get_directory_modules_for_name(mods, search_name): + try: + stmts = m.used_names[search_name] + except KeyError: + continue + for stmt in stmts: + if isinstance(stmt, pr.Import): + count = 0 + imps = [] + for i in stmt.get_all_import_names(): + for name_part in i.names: + count += 1 + if name_part == search_name: + imps.append((count, name_part)) + + for used_count, name_part in imps: + i = imports.ImportPath(_evaluator, stmt, kill_count=count - used_count, + direct_resolve=True) + f = i.follow(is_goto=True) + if set(f) & set(definitions): + names.append(api_classes.Usage(name_part, stmt)) + else: + for call in _scan_statement(stmt, search_name, + assignment_details=True): + names += check_call(call) + return names + + +def usages_add_import_modules(definitions, search_name): + """ Adds the modules of the imports """ + new = set() + for d in definitions: + if isinstance(d.parent, pr.Import): + s = imports.ImportPath(_evaluator, d.parent, direct_resolve=True) + with common.ignored(IndexError): + new.add(s.follow(is_goto=True)[0]) + return set(definitions) | new + + diff --git a/jedi/evaluate/dynamic.py b/jedi/evaluate/dynamic.py index 9b330b6c..cf688eac 100644 --- a/jedi/evaluate/dynamic.py +++ b/jedi/evaluate/dynamic.py @@ -57,10 +57,8 @@ from jedi import cache from jedi.parser import representation as pr from jedi import modules from jedi import settings -from jedi import common from jedi import debug from jedi.parser import fast as fast_parser -from jedi.evaluate import imports from jedi.evaluate.cache import memoize_default # This is something like the sys.path, but only for searching params. It means @@ -449,83 +447,6 @@ class ArrayInstance(pr.Base): return items -def usages(definitions, search_name, mods): - def compare_array(definitions): - """ `definitions` are being compared by module/start_pos, because - sometimes the id's of the objects change (e.g. executions). - """ - result = [] - for d in definitions: - module = d.get_parent_until() - result.append((module, d.start_pos)) - return result - - def check_call(call): - result = [] - follow = [] # There might be multiple search_name's in one call_path - call_path = list(call.generate_call_path()) - for i, name in enumerate(call_path): - # name is `pr.NamePart`. - if name == search_name: - follow.append(call_path[:i + 1]) - - for f in follow: - follow_res, search = evaluate.goto(call.parent, f) - follow_res = usages_add_import_modules(follow_res, search) - - compare_follow_res = compare_array(follow_res) - # compare to see if they match - if any(r in compare_definitions for r in compare_follow_res): - scope = call.parent - result.append(api_classes.Usage(search, scope)) - - return result - - if not definitions: - return set() - - compare_definitions = compare_array(definitions) - mods |= set([d.get_parent_until() for d in definitions]) - names = [] - for m in get_directory_modules_for_name(mods, search_name): - try: - stmts = m.used_names[search_name] - except KeyError: - continue - for stmt in stmts: - if isinstance(stmt, pr.Import): - count = 0 - imps = [] - for i in stmt.get_all_import_names(): - for name_part in i.names: - count += 1 - if name_part == search_name: - imps.append((count, name_part)) - - for used_count, name_part in imps: - i = imports.ImportPath(_evaluator, stmt, kill_count=count - used_count, - direct_resolve=True) - f = i.follow(is_goto=True) - if set(f) & set(definitions): - names.append(api_classes.Usage(name_part, stmt)) - else: - for call in _scan_statement(stmt, search_name, - assignment_details=True): - names += check_call(call) - return names - - -def usages_add_import_modules(definitions, search_name): - """ Adds the modules of the imports """ - new = set() - for d in definitions: - if isinstance(d.parent, pr.Import): - s = imports.ImportPath(_evaluator, d.parent, direct_resolve=True) - with common.ignored(IndexError): - new.add(s.follow(is_goto=True)[0]) - return set(definitions) | new - - def check_flow_information(flow, search_name, pos): """ Try to find out the type of a variable just with the information that is given by the flows: e.g. It is also responsible for assert checks.::