mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-06 14:04:26 +08:00
Refactor references: Matching more names that might be related
Fixes davidhalter/jedi-vim#900. See also davidhalter/jedi-vim#552.
This commit is contained in:
@@ -308,11 +308,14 @@ class ModuleContext(TreeContextMixin, ValueContext):
|
|||||||
until_position=until_position,
|
until_position=until_position,
|
||||||
origin_scope=origin_scope
|
origin_scope=origin_scope
|
||||||
),
|
),
|
||||||
GlobalNameFilter(self, self.tree_node),
|
self.get_global_filter(),
|
||||||
)
|
)
|
||||||
for f in filters: # Python 2...
|
for f in filters: # Python 2...
|
||||||
yield f
|
yield f
|
||||||
|
|
||||||
|
def get_global_filter(self):
|
||||||
|
return GlobalNameFilter(self, self.tree_node)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def string_names(self):
|
def string_names(self):
|
||||||
return self._value.string_names
|
return self._value.string_names
|
||||||
|
|||||||
@@ -564,7 +564,7 @@ class ImportName(AbstractNameDefinition):
|
|||||||
return m
|
return m
|
||||||
# It's almost always possible to find the import or to not find it. The
|
# It's almost always possible to find the import or to not find it. The
|
||||||
# importing returns only one value, pretty much always.
|
# importing returns only one value, pretty much always.
|
||||||
return next(iter(import_values))
|
return next(iter(import_values)).as_context()
|
||||||
|
|
||||||
@memoize_method
|
@memoize_method
|
||||||
def infer(self):
|
def infer(self):
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from jedi.inference import imports
|
from jedi.inference import imports
|
||||||
|
from jedi.inference.filters import ParserTreeFilter
|
||||||
|
|
||||||
|
|
||||||
def _resolve_names(definition_names, avoid_names=()):
|
def _resolve_names(definition_names, avoid_names=()):
|
||||||
@@ -25,17 +26,68 @@ def _dictionarize(names):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _find_defining_names(module_context, tree_name):
|
||||||
|
found_names = _find_names(module_context, tree_name)
|
||||||
|
|
||||||
|
found_names |= set(_find_global_variables(found_names, tree_name.value))
|
||||||
|
for name in list(found_names):
|
||||||
|
if name.api_type == 'param' or name.tree_name is None \
|
||||||
|
or name.tree_name.parent.type == 'trailer':
|
||||||
|
continue
|
||||||
|
found_names |= set(_add_names_in_same_context(name.parent_context, name.string_name))
|
||||||
|
return set(_resolve_names(found_names))
|
||||||
|
|
||||||
|
|
||||||
def _find_names(module_context, tree_name):
|
def _find_names(module_context, tree_name):
|
||||||
name = module_context.create_name(tree_name)
|
name = module_context.create_name(tree_name)
|
||||||
found_names = set(name.goto())
|
found_names = set(name.goto())
|
||||||
found_names.add(name)
|
found_names.add(name)
|
||||||
return _dictionarize(_resolve_names(found_names))
|
|
||||||
|
return set(_resolve_names(found_names))
|
||||||
|
|
||||||
|
|
||||||
|
def _add_names_in_same_context(context, string_name):
|
||||||
|
if context.tree_node is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
until_position = None
|
||||||
|
while True:
|
||||||
|
filter_ = ParserTreeFilter(
|
||||||
|
parent_context=context,
|
||||||
|
until_position=until_position,
|
||||||
|
)
|
||||||
|
names = set(filter_.get(string_name))
|
||||||
|
if not names:
|
||||||
|
break
|
||||||
|
for name in names:
|
||||||
|
yield name
|
||||||
|
ordered = sorted(names, key=lambda x: x.start_pos)
|
||||||
|
until_position = ordered[0].start_pos
|
||||||
|
|
||||||
|
|
||||||
|
def _find_global_variables(names, search_name):
|
||||||
|
for name in names:
|
||||||
|
if name.tree_name is None:
|
||||||
|
continue
|
||||||
|
module_context = name.get_root_context()
|
||||||
|
try:
|
||||||
|
method = module_context.get_global_filter
|
||||||
|
except AttributeError:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
for global_name in method().get(search_name):
|
||||||
|
yield global_name
|
||||||
|
c = module_context.create_context(global_name.tree_name)
|
||||||
|
for name in _add_names_in_same_context(c, global_name.string_name):
|
||||||
|
yield name
|
||||||
|
|
||||||
|
|
||||||
def find_references(module_context, tree_name):
|
def find_references(module_context, tree_name):
|
||||||
search_name = tree_name.value
|
search_name = tree_name.value
|
||||||
found_names = _find_names(module_context, tree_name)
|
found_names = _find_defining_names(module_context, tree_name)
|
||||||
module_contexts = set(d.get_root_context() for d in found_names.values())
|
found_names_dct = _dictionarize(found_names)
|
||||||
|
|
||||||
|
module_contexts = set(d.get_root_context() for d in found_names)
|
||||||
module_contexts = set(m for m in module_contexts if not m.is_compiled())
|
module_contexts = set(m for m in module_contexts if not m.is_compiled())
|
||||||
|
|
||||||
non_matching_reference_maps = {}
|
non_matching_reference_maps = {}
|
||||||
@@ -45,14 +97,14 @@ def find_references(module_context, tree_name):
|
|||||||
)
|
)
|
||||||
for module_context in potential_modules:
|
for module_context in potential_modules:
|
||||||
for name_leaf in module_context.tree_node.get_used_names().get(search_name, []):
|
for name_leaf in module_context.tree_node.get_used_names().get(search_name, []):
|
||||||
new = _find_names(module_context, name_leaf)
|
new = _dictionarize(_find_names(module_context, name_leaf))
|
||||||
if any(tree_name in found_names for tree_name in new):
|
if any(tree_name in found_names_dct for tree_name in new):
|
||||||
found_names.update(new)
|
found_names_dct.update(new)
|
||||||
for tree_name in new:
|
for tree_name in new:
|
||||||
for dct in non_matching_reference_maps.get(tree_name, []):
|
for dct in non_matching_reference_maps.get(tree_name, []):
|
||||||
# A reference that was previously searched for matches
|
# A reference that was previously searched for matches
|
||||||
# with a now found name. Merge.
|
# with a now found name. Merge.
|
||||||
found_names.update(dct)
|
found_names_dct.update(dct)
|
||||||
try:
|
try:
|
||||||
del non_matching_reference_maps[tree_name]
|
del non_matching_reference_maps[tree_name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -60,4 +112,4 @@ def find_references(module_context, tree_name):
|
|||||||
else:
|
else:
|
||||||
for name in new:
|
for name in new:
|
||||||
non_matching_reference_maps.setdefault(name, []).append(new)
|
non_matching_reference_maps.setdefault(name, []).append(new)
|
||||||
return found_names.values()
|
return found_names_dct.values()
|
||||||
|
|||||||
@@ -97,9 +97,6 @@ class FunctionMixin(object):
|
|||||||
|
|
||||||
|
|
||||||
class FunctionValue(use_metaclass(CachedMetaClass, FunctionMixin, FunctionAndClassBase)):
|
class FunctionValue(use_metaclass(CachedMetaClass, FunctionMixin, FunctionAndClassBase)):
|
||||||
def is_function(self):
|
|
||||||
return True
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_context(cls, context, tree_node):
|
def from_context(cls, context, tree_node):
|
||||||
def create(tree_node):
|
def create(tree_node):
|
||||||
@@ -162,6 +159,9 @@ class MethodValue(FunctionValue):
|
|||||||
|
|
||||||
|
|
||||||
class BaseFunctionExecutionContext(ValueContext, TreeContextMixin):
|
class BaseFunctionExecutionContext(ValueContext, TreeContextMixin):
|
||||||
|
def is_function_execution(self):
|
||||||
|
return True
|
||||||
|
|
||||||
def _infer_annotations(self):
|
def _infer_annotations(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|||||||
@@ -3,34 +3,34 @@ Renaming tests. This means search for references.
|
|||||||
I always leave a little bit of space to add room for additions, because the
|
I always leave a little bit of space to add room for additions, because the
|
||||||
results always contain position informations.
|
results always contain position informations.
|
||||||
"""
|
"""
|
||||||
#< 4 (0,4), (3,0), (5,0), (17,0), (12,4), (14,5), (15,0)
|
#< 4 (0,4), (3,0), (5,0), (12,4), (14,5), (15,0), (17,0), (19,0)
|
||||||
def abc(): pass
|
def abcd(): pass
|
||||||
|
|
||||||
#< 0 (-3,4), (0,0), (2,0), (14,0), (9,4), (11,5), (12,0)
|
#< 0 (-3,4), (0,0), (2,0), (9,4), (11,5), (12,0), (14,0), (16,0)
|
||||||
abc.d.a.bsaasd.abc.d
|
abcd.d.a.bsaasd.abcd.d
|
||||||
|
|
||||||
abc
|
abcd
|
||||||
# unicode chars shouldn't be a problem.
|
# unicode chars shouldn't be a problem.
|
||||||
x['smörbröd'].abc
|
x['smörbröd'].abcd
|
||||||
|
|
||||||
# With the new parser these statements are not recognized as stateents, because
|
# With the new parser these statements are not recognized as stateents, because
|
||||||
# they are not valid Python.
|
# they are not valid Python.
|
||||||
if 1:
|
if 1:
|
||||||
abc =
|
abcd =
|
||||||
else:
|
else:
|
||||||
(abc) =
|
(abcd) =
|
||||||
abc =
|
abcd =
|
||||||
#< (-17,4), (-14,0), (-12,0), (0,0), (-2,0), (-3,5), (-5,4)
|
#< (-17,4), (-14,0), (-12,0), (0,0), (2,0), (-2,0), (-3,5), (-5,4)
|
||||||
abc
|
abcd
|
||||||
|
|
||||||
abc = 5
|
abcd = 5
|
||||||
|
|
||||||
|
|
||||||
Abc = 3
|
Abc = 3
|
||||||
|
|
||||||
#< 6 (0,6), (2,4), (5,8), (17,0)
|
#< 6 (-3,0), (0,6), (2,4), (5,8), (17,0)
|
||||||
class Abc():
|
class Abc():
|
||||||
#< (-2,6), (0,4), (3,8), (15,0)
|
#< (-5,0), (-2,6), (0,4), (2,8), (3,8), (15,0)
|
||||||
Abc
|
Abc
|
||||||
|
|
||||||
def Abc(self):
|
def Abc(self):
|
||||||
@@ -65,11 +65,11 @@ set_object_var.var = 1
|
|||||||
|
|
||||||
|
|
||||||
response = 5
|
response = 5
|
||||||
#< 0 (0,0), (1,0), (2,0), (4,0)
|
#< 0 (-2,0), (0,0), (1,0), (2,0), (4,0)
|
||||||
response = HttpResponse(mimetype='application/pdf')
|
response = HttpResponse(mimetype='application/pdf')
|
||||||
response['Content-Disposition'] = 'attachment; filename=%s.pdf' % id
|
response['Content-Disposition'] = 'attachment; filename=%s.pdf' % id
|
||||||
response.write(pdf)
|
response.write(pdf)
|
||||||
#< (-4,0), (-3,0), (-2,0), (0,0)
|
#< (-6,0), (-4,0), (-3,0), (-2,0), (0,0)
|
||||||
response
|
response
|
||||||
|
|
||||||
|
|
||||||
@@ -215,18 +215,18 @@ class TestProperty:
|
|||||||
self.prop
|
self.prop
|
||||||
|
|
||||||
@property
|
@property
|
||||||
#< 13 (0,8), (4,5)
|
#< 13 (0,8), (4,5), (6,8), (11,13)
|
||||||
def rw_prop(self):
|
def rw_prop(self):
|
||||||
return self._rw_prop
|
return self._rw_prop
|
||||||
|
|
||||||
#< 8 (-4,8), (0,5)
|
#< 8 (-4,8), (0,5), (2,8), (7,13)
|
||||||
@rw_prop.setter
|
@rw_prop.setter
|
||||||
#< 8 (0,8), (5,13)
|
#< 8 (-6,8), (-2,5), (0,8), (5,13)
|
||||||
def rw_prop(self, value):
|
def rw_prop(self, value):
|
||||||
self._rw_prop = value
|
self._rw_prop = value
|
||||||
|
|
||||||
def b(self):
|
def b(self):
|
||||||
#< 13 (-5,8), (0,13)
|
#< 13 (-11,8), (-7,5), (-5,8), (0,13)
|
||||||
self.rw_prop
|
self.rw_prop
|
||||||
|
|
||||||
# -----------------
|
# -----------------
|
||||||
@@ -287,9 +287,9 @@ x = 32
|
|||||||
[x for x in x]
|
[x for x in x]
|
||||||
|
|
||||||
#< 0 (0,0), (2,1), (2,12)
|
#< 0 (0,0), (2,1), (2,12)
|
||||||
x = 32
|
y = 32
|
||||||
#< 12 (-2,0), (0,1), (0,12)
|
#< 12 (-2,0), (0,1), (0,12)
|
||||||
[x for b in x]
|
[y for b in y]
|
||||||
|
|
||||||
|
|
||||||
#< 1 (0,1), (0,7)
|
#< 1 (0,1), (0,7)
|
||||||
@@ -297,13 +297,13 @@ x = 32
|
|||||||
#< 7 (0,1), (0,7)
|
#< 7 (0,1), (0,7)
|
||||||
[x for x in something]
|
[x for x in something]
|
||||||
|
|
||||||
x = 3
|
z = 3
|
||||||
#< 1 (0,1), (0,10)
|
#< 1 (0,1), (0,10)
|
||||||
{x:1 for x in something}
|
{z:1 for z in something}
|
||||||
#< 10 (0,1), (0,10)
|
#< 10 (0,1), (0,10)
|
||||||
{x:1 for x in something}
|
{z:1 for z in something}
|
||||||
|
|
||||||
def x():
|
def whatever_func():
|
||||||
zzz = 3
|
zzz = 3
|
||||||
if UNDEFINED:
|
if UNDEFINED:
|
||||||
zzz = 5
|
zzz = 5
|
||||||
@@ -314,3 +314,31 @@ def x():
|
|||||||
#< (0, 8), (1, 4), (-3, 12), (-6, 8), (-8, 4)
|
#< (0, 8), (1, 4), (-3, 12), (-6, 8), (-8, 4)
|
||||||
zzz
|
zzz
|
||||||
zzz
|
zzz
|
||||||
|
|
||||||
|
# -----------------
|
||||||
|
# global
|
||||||
|
# -----------------
|
||||||
|
|
||||||
|
def global_usage1():
|
||||||
|
#< (0, 4), (4, 11), (6, 4), (9, 8), (12, 4)
|
||||||
|
my_global
|
||||||
|
|
||||||
|
def global_definition():
|
||||||
|
#< (-4, 4), (0, 11), (2, 4), (5, 8), (8, 4)
|
||||||
|
global my_global
|
||||||
|
#< 4 (-6, 4), (-2, 11), (0, 4), (3, 8), (6, 4)
|
||||||
|
my_global = 3
|
||||||
|
if WHATEVER:
|
||||||
|
#< 8 (-9, 4), (-5, 11), (-3, 4), (0, 8), (3, 4)
|
||||||
|
my_global = 4
|
||||||
|
|
||||||
|
def global_usage2()
|
||||||
|
my_global
|
||||||
|
|
||||||
|
def not_global(my_global):
|
||||||
|
my_global
|
||||||
|
|
||||||
|
class DefinitelyNotGlobal:
|
||||||
|
def my_global(self):
|
||||||
|
def my_global(self):
|
||||||
|
pass
|
||||||
|
|||||||
Reference in New Issue
Block a user