forked from VimPlug/jedi
Instead of having both next and execution as attributes, we now only have next, because it's an execution if there's an array.
98 lines
4.0 KiB
Python
98 lines
4.0 KiB
Python
from jedi._compatibility import u, unicode
|
|
from jedi import common
|
|
from jedi.api import classes
|
|
from jedi.parser import representation as pr
|
|
from jedi.evaluate import imports
|
|
from jedi.evaluate import helpers
|
|
|
|
|
|
def usages(evaluator, 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_for_usage(call):
|
|
stmt = call.parent
|
|
while not stmt.parent.is_scope():
|
|
stmt = stmt.parent
|
|
# New definition, call cannot be a part of stmt
|
|
if len(call.name) == 1 and call.next is None \
|
|
and call.name in stmt.get_defined_names():
|
|
# Class params are not definitions (like function params). They
|
|
# are super classes, that need to be resolved.
|
|
if not (isinstance(stmt, pr.Param) and isinstance(stmt.parent, pr.Class)):
|
|
return
|
|
|
|
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 u(name) == search_name:
|
|
follow.append(call_path[:i + 1])
|
|
|
|
for call_path in follow:
|
|
follow_res, search = evaluator.goto(call.parent, call_path)
|
|
# names can change (getattr stuff), therefore filter names that
|
|
# don't match `search`.
|
|
|
|
# TODO add something like that in the future - for now usages are
|
|
# completely broken anyway.
|
|
#follow_res = [r for r in follow_res if str(r) == search]
|
|
#print search.start_pos,search_name.start_pos
|
|
#print follow_res, search, search_name, [(r, r.start_pos) for r in follow_res]
|
|
follow_res = usages_add_import_modules(evaluator, 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):
|
|
yield classes.Definition(evaluator, search)
|
|
|
|
if not definitions:
|
|
return set()
|
|
|
|
compare_definitions = compare_array(definitions)
|
|
mods |= set([d.get_parent_until() for d in definitions])
|
|
names = []
|
|
for m in imports.get_modules_containing_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 unicode(name_part) == search_name:
|
|
imps.append((count, name_part))
|
|
|
|
for used_count, name_part in imps:
|
|
i = imports.ImportWrapper(evaluator, stmt, kill_count=count - used_count,
|
|
nested_resolve=True)
|
|
f = i.follow(is_goto=True)
|
|
if set(f) & set(definitions):
|
|
names.append(classes.Definition(evaluator, name_part))
|
|
else:
|
|
for call in helpers.scan_statement_for_calls(stmt, search_name, assignment_details=True):
|
|
names += check_call_for_usage(call)
|
|
return names
|
|
|
|
|
|
def usages_add_import_modules(evaluator, definitions, search_name):
|
|
""" Adds the modules of the imports """
|
|
new = set()
|
|
for d in definitions:
|
|
if isinstance(d.parent, pr.Import):
|
|
s = imports.ImportWrapper(evaluator, d.parent, nested_resolve=True)
|
|
with common.ignored(IndexError):
|
|
new.add(s.follow(is_goto=True)[0])
|
|
return set(definitions) | new
|