mirror of
https://github.com/davidhalter/jedi.git
synced 2026-02-15 18:12:00 +08:00
Simplify usages. It should also work way better, now.
This commit is contained in:
@@ -22,11 +22,11 @@ from jedi import settings
|
||||
from jedi import cache
|
||||
from jedi.api import classes
|
||||
from jedi.api import interpreter
|
||||
from jedi.api import usages
|
||||
from jedi.api import helpers
|
||||
from jedi.api.completion import Completion
|
||||
from jedi.evaluate import Evaluator
|
||||
from jedi.evaluate import imports
|
||||
from jedi.evaluate import usages
|
||||
from jedi.evaluate.project import Project
|
||||
from jedi.evaluate.arguments import try_iter_content
|
||||
from jedi.evaluate.helpers import get_module_names, evaluate_call_of_leaf
|
||||
@@ -205,7 +205,12 @@ class Script(object):
|
||||
else:
|
||||
yield name
|
||||
|
||||
names = self._goto()
|
||||
tree_name = self._get_module_node().get_name_of_position(self._pos)
|
||||
if tree_name is None:
|
||||
return []
|
||||
context = self._evaluator.create_context(self._get_module(), tree_name)
|
||||
names = list(self._evaluator.goto(context, tree_name))
|
||||
|
||||
if follow_imports:
|
||||
def check(name):
|
||||
if isinstance(name, ModuleName):
|
||||
@@ -220,16 +225,6 @@ class Script(object):
|
||||
defs = [classes.Definition(self._evaluator, d) for d in set(names)]
|
||||
return helpers.sorted_definitions(defs)
|
||||
|
||||
def _goto(self):
|
||||
"""
|
||||
Used for goto_assignments and usages.
|
||||
"""
|
||||
name = self._get_module_node().get_name_of_position(self._pos)
|
||||
if name is None:
|
||||
return []
|
||||
context = self._evaluator.create_context(self._get_module(), name)
|
||||
return list(self._evaluator.goto(context, name))
|
||||
|
||||
def usages(self, additional_module_paths=()):
|
||||
"""
|
||||
Return :class:`classes.Definition` objects, which contain all
|
||||
@@ -241,31 +236,15 @@ class Script(object):
|
||||
|
||||
:rtype: list of :class:`classes.Definition`
|
||||
"""
|
||||
module_node = self._get_module_node()
|
||||
user_stmt = get_statement_of_position(module_node, self._pos)
|
||||
definition_names = self._goto()
|
||||
if not definition_names and isinstance(user_stmt, tree.Import):
|
||||
# For not defined imports (goto doesn't find something, we take
|
||||
# the name as a definition. This is enough, because every name
|
||||
# points to it.
|
||||
name = user_stmt.get_name_of_position(self._pos)
|
||||
if name is None:
|
||||
# Must be syntax
|
||||
return []
|
||||
definition_names = [TreeNameDefinition(self._get_module(), name)]
|
||||
|
||||
if not definition_names:
|
||||
# Without a definition for a name we cannot find references.
|
||||
tree_name = self._get_module_node().get_name_of_position(self._pos)
|
||||
if tree_name is None:
|
||||
# Must be syntax
|
||||
return []
|
||||
|
||||
definition_names = usages.resolve_potential_imports(self._evaluator,
|
||||
definition_names)
|
||||
names = usages.usages(self._evaluator, self._get_module(), tree_name)
|
||||
|
||||
modules = set([d.get_root_context() for d in definition_names])
|
||||
modules.add(self._get_module())
|
||||
definitions = usages.usages(self._evaluator, definition_names, modules)
|
||||
|
||||
return helpers.sorted_definitions(set(definitions))
|
||||
definitions = [classes.Definition(self._evaluator, n) for n in names]
|
||||
return helpers.sorted_definitions(definitions)
|
||||
|
||||
def call_signatures(self):
|
||||
"""
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
from jedi.api import classes
|
||||
from parso.python import tree
|
||||
from jedi.evaluate import imports
|
||||
from jedi.evaluate.filters import TreeNameDefinition
|
||||
from jedi.evaluate.context import ModuleContext
|
||||
|
||||
|
||||
def compare_contexts(c1, c2):
|
||||
return c1 == c2 or (c1[1] == c2[1] and c1[0].tree_node == c2[0].tree_node)
|
||||
|
||||
|
||||
def usages(evaluator, definition_names, mods):
|
||||
"""
|
||||
:param definitions: list of Name
|
||||
"""
|
||||
def resolve_names(definition_names):
|
||||
for name in definition_names:
|
||||
if name.api_type == 'module':
|
||||
found = False
|
||||
for context in name.infer():
|
||||
if isinstance(context, ModuleContext):
|
||||
found = True
|
||||
yield context.name
|
||||
if not found:
|
||||
yield name
|
||||
else:
|
||||
yield name
|
||||
|
||||
def compare_array(definition_names):
|
||||
""" `definitions` are being compared by module/start_pos, because
|
||||
sometimes the id's of the objects change (e.g. executions).
|
||||
"""
|
||||
return [
|
||||
(name.get_root_context(), name.start_pos)
|
||||
for name in resolve_names(definition_names)
|
||||
]
|
||||
|
||||
search_name = list(definition_names)[0].string_name
|
||||
compare_definitions = compare_array(definition_names)
|
||||
mods = mods | set([d.get_root_context() for d in definition_names])
|
||||
definition_names = set(resolve_names(definition_names))
|
||||
for m in imports.get_modules_containing_name(evaluator, mods, search_name):
|
||||
if isinstance(m, ModuleContext):
|
||||
for name_node in m.tree_node.get_used_names().get(search_name, []):
|
||||
context = evaluator.create_context(m, name_node)
|
||||
result = evaluator.goto(context, name_node)
|
||||
if any(compare_contexts(c1, c2)
|
||||
for c1 in compare_array(result)
|
||||
for c2 in compare_definitions):
|
||||
name = TreeNameDefinition(context, name_node)
|
||||
definition_names.add(name)
|
||||
# Previous definitions might be imports, so include them
|
||||
# (because goto might return that import name).
|
||||
compare_definitions += compare_array([name])
|
||||
else:
|
||||
# compiled objects
|
||||
definition_names.add(m.name)
|
||||
|
||||
return [classes.Definition(evaluator, n) for n in definition_names]
|
||||
|
||||
|
||||
def resolve_potential_imports(evaluator, definitions):
|
||||
""" Adds the modules of the imports """
|
||||
new = set()
|
||||
for d in definitions:
|
||||
if isinstance(d, TreeNameDefinition):
|
||||
imp_or_stmt = d.tree_name.get_definition()
|
||||
if isinstance(imp_or_stmt, tree.Import):
|
||||
new |= resolve_potential_imports(
|
||||
evaluator,
|
||||
set(imports.infer_import(
|
||||
d.parent_context, d.tree_name, is_goto=True
|
||||
))
|
||||
)
|
||||
return set(definitions) | new
|
||||
50
jedi/evaluate/usages.py
Normal file
50
jedi/evaluate/usages.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from jedi.evaluate import imports
|
||||
from jedi.evaluate.filters import TreeNameDefinition
|
||||
from jedi.evaluate.context import ModuleContext
|
||||
|
||||
|
||||
def usages(evaluator, module_context, tree_name):
|
||||
"""
|
||||
:param definitions: list of Name
|
||||
"""
|
||||
def resolve_names(definition_names, avoid_names=()):
|
||||
for name in definition_names:
|
||||
if name in avoid_names:
|
||||
# Avoiding recursions here, because goto on a module name lands
|
||||
# on the same module.
|
||||
continue
|
||||
|
||||
if not isinstance(name, imports.SubModuleName):
|
||||
# SubModuleNames are not actually existing names but created
|
||||
# names when importing something like `import foo.bar.baz`.
|
||||
yield name
|
||||
|
||||
if name.api_type == 'module':
|
||||
for name in resolve_names(name.goto(), definition_names):
|
||||
yield name
|
||||
|
||||
def find_names(module_context, tree_name):
|
||||
context = evaluator.create_context(module_context, tree_name)
|
||||
name = TreeNameDefinition(context, tree_name)
|
||||
found_names = set(name.goto())
|
||||
found_names.add(name)
|
||||
return dcti(resolve_names(found_names))
|
||||
|
||||
def dcti(names):
|
||||
return dict(
|
||||
(n if n.tree_name is None else n.tree_name, n)
|
||||
for n in names
|
||||
)
|
||||
|
||||
search_name = tree_name.value
|
||||
found_names = find_names(module_context, tree_name)
|
||||
modules = set(d.get_root_context() for d in found_names.values())
|
||||
modules = set(m for m in modules if isinstance(m, ModuleContext))
|
||||
for m in imports.get_modules_containing_name(evaluator, modules, search_name):
|
||||
for name_leaf in m.tree_node.get_used_names().get(search_name, []):
|
||||
new = find_names(m, name_leaf)
|
||||
for tree_name in new:
|
||||
if tree_name in found_names:
|
||||
found_names.update(new)
|
||||
break
|
||||
return found_names.values()
|
||||
Reference in New Issue
Block a user