forked from VimPlug/jedi
Make goto_definitions a lot simpler.
This commit is contained in:
@@ -183,41 +183,9 @@ class Script(object):
|
|||||||
|
|
||||||
:rtype: list of :class:`classes.Definition`
|
:rtype: list of :class:`classes.Definition`
|
||||||
"""
|
"""
|
||||||
def resolve_import_paths(definitions):
|
c = helpers.ContextResults(self._evaluator, self.source, self._get_module(), self._pos)
|
||||||
new_defs = list(definitions)
|
definitions = c.get_results()
|
||||||
for s in definitions:
|
|
||||||
if isinstance(s, imports.ImportWrapper):
|
|
||||||
new_defs.remove(s)
|
|
||||||
new_defs += resolve_import_paths(set(s.follow()))
|
|
||||||
return new_defs
|
|
||||||
|
|
||||||
goto_path = self._user_context.get_path_under_cursor()
|
|
||||||
context = self._user_context.get_reverse_context()
|
|
||||||
definitions = []
|
|
||||||
if next(context) in ('class', 'def'):
|
|
||||||
definitions = [self._evaluator.wrap(self._parser.user_scope())]
|
|
||||||
else:
|
|
||||||
# Fetch definition of callee, if there's no path otherwise.
|
|
||||||
if not goto_path:
|
|
||||||
definitions = [signature._definition
|
|
||||||
for signature in self.call_signatures()]
|
|
||||||
|
|
||||||
if re.match('\w[\w\d_]*$', goto_path) and not definitions:
|
|
||||||
user_stmt = self._parser.user_stmt()
|
|
||||||
if user_stmt is not None and user_stmt.type == 'expr_stmt':
|
|
||||||
for name in user_stmt.get_defined_names():
|
|
||||||
if name.start_pos <= self._pos <= name.end_pos:
|
|
||||||
# TODO scaning for a name and then using it should be
|
|
||||||
# the default.
|
|
||||||
definitions = self._evaluator.goto_definition(name)
|
|
||||||
|
|
||||||
if not definitions and goto_path:
|
|
||||||
definitions = inference.type_inference(
|
|
||||||
self._evaluator, self._parser, self._user_context,
|
|
||||||
self._pos, goto_path
|
|
||||||
)
|
|
||||||
|
|
||||||
definitions = resolve_import_paths(definitions)
|
|
||||||
names = [s.name for s in definitions]
|
names = [s.name for s in definitions]
|
||||||
defs = [classes.Definition(self._evaluator, name) for name in names]
|
defs = [classes.Definition(self._evaluator, name) for name in names]
|
||||||
# The additional set here allows the definitions to become unique in an
|
# The additional set here allows the definitions to become unique in an
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ class Completion:
|
|||||||
else:
|
else:
|
||||||
scopes = list(inference.type_inference(
|
scopes = list(inference.type_inference(
|
||||||
self._evaluator, self._parser, self._user_context,
|
self._evaluator, self._parser, self._user_context,
|
||||||
self._pos, completion_parts.path, is_completion=True
|
self._pos, completion_parts.path
|
||||||
))
|
))
|
||||||
completion_names = []
|
completion_names = []
|
||||||
debug.dbg('possible completion scopes: %s', scopes)
|
debug.dbg('possible completion scopes: %s', scopes)
|
||||||
|
|||||||
@@ -212,3 +212,70 @@ def importer_from_error_statement(error_statement, pos):
|
|||||||
only_modules = False
|
only_modules = False
|
||||||
|
|
||||||
return names, level, only_modules, unfinished_dotted
|
return names, level, only_modules, unfinished_dotted
|
||||||
|
|
||||||
|
|
||||||
|
class ContextResults():
|
||||||
|
def __init__(self, evaluator, source, module, pos):
|
||||||
|
self._evaluator = evaluator
|
||||||
|
self._module = module
|
||||||
|
self._source = source
|
||||||
|
self._pos = pos
|
||||||
|
|
||||||
|
def _on_defining_name(self, leaf):
|
||||||
|
return [self._evaluator.wrap(self._parser.user_scope())]
|
||||||
|
|
||||||
|
def get_results(self):
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
stack = get_stack_at_position(self._evaluator.grammar, self._source, self._module, self._leaf.end_pos)
|
||||||
|
except OnErrorLeaf:
|
||||||
|
return []
|
||||||
|
'''
|
||||||
|
|
||||||
|
name = self._module.name_for_position(self._pos)
|
||||||
|
if name is not None:
|
||||||
|
return self._evaluator.goto_definition(name)
|
||||||
|
|
||||||
|
leaf = self._module.get_leaf_for_position(self._pos)
|
||||||
|
if leaf is None:
|
||||||
|
return []
|
||||||
|
|
||||||
|
if leaf.parent.type == 'atom':
|
||||||
|
return self._evaluator.eval_element(leaf.parent)
|
||||||
|
if leaf.parent.type == 'trailer':
|
||||||
|
return self._evaluator.eval_element(leaf.parent.parent)
|
||||||
|
return []
|
||||||
|
symbol_names = list(stack.get_node_names(self._evaluator.grammar))
|
||||||
|
|
||||||
|
nodes = list(stack.get_nodes())
|
||||||
|
|
||||||
|
if "import_stmt" in symbol_names:
|
||||||
|
level = 0
|
||||||
|
only_modules = True
|
||||||
|
level, names = self._parse_dotted_names(nodes)
|
||||||
|
if "import_from" in symbol_names:
|
||||||
|
if 'import' in nodes:
|
||||||
|
only_modules = False
|
||||||
|
else:
|
||||||
|
assert "import_name" in symbol_names
|
||||||
|
|
||||||
|
completion_names += self._get_importer_names(
|
||||||
|
names,
|
||||||
|
level,
|
||||||
|
only_modules
|
||||||
|
)
|
||||||
|
elif nodes[-2] in ('as', 'def', 'class'):
|
||||||
|
# No completions for ``with x as foo`` and ``import x as foo``.
|
||||||
|
# Also true for defining names as a class or function.
|
||||||
|
return self._on_defining_name(self._leaf)
|
||||||
|
else:
|
||||||
|
completion_names += self._simple_complete(completion_parts)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
class GotoDefinition(ContextResults):
|
||||||
|
def _():
|
||||||
|
definitions = inference.type_inference(
|
||||||
|
self._evaluator, self._parser, self._user_context,
|
||||||
|
self._pos, goto_path
|
||||||
|
)
|
||||||
|
|||||||
@@ -3,50 +3,29 @@ This module has helpers for doing type inference on strings. It is needed,
|
|||||||
because we still want to infer types where the syntax is invalid.
|
because we still want to infer types where the syntax is invalid.
|
||||||
"""
|
"""
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi.api import helpers
|
|
||||||
from jedi.parser import tree
|
|
||||||
from jedi.parser import Parser, ParseError
|
from jedi.parser import Parser, ParseError
|
||||||
from jedi.evaluate import imports
|
|
||||||
from jedi.evaluate.cache import memoize_default
|
from jedi.evaluate.cache import memoize_default
|
||||||
|
from jedi.api import helpers
|
||||||
|
|
||||||
|
|
||||||
def type_inference(evaluator, parser, user_context, position, dotted_path, is_completion=False):
|
def goto_checks(evaluator, parser, user_context, position, dotted_path, follow_types=False):
|
||||||
|
module = evaluator.wrap(parser.module())
|
||||||
|
stack = helpers.get_stack_at_position(evaluator.grammar, self._source, module, position)
|
||||||
|
stack
|
||||||
|
|
||||||
|
def type_inference(evaluator, parser, user_context, position, dotted_path):
|
||||||
"""
|
"""
|
||||||
Base for completions/goto. Basically it returns the resolved scopes
|
Base for completions/goto. Basically it returns the resolved scopes
|
||||||
under cursor.
|
under cursor.
|
||||||
"""
|
"""
|
||||||
debug.dbg('start: %s in %s', dotted_path, parser.user_scope())
|
debug.dbg('start: %s in %s', dotted_path, parser.user_scope())
|
||||||
|
|
||||||
user_stmt = parser.user_stmt_with_whitespace()
|
# Just parse one statement, take it and evaluate it.
|
||||||
if not user_stmt and len(dotted_path.split('\n')) > 1:
|
eval_stmt = get_under_cursor_stmt(evaluator, parser, dotted_path, position)
|
||||||
# If the user_stmt is not defined and the dotted_path is multi line,
|
if eval_stmt is None:
|
||||||
# something's strange. Most probably the backwards tokenizer
|
|
||||||
# matched to much.
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
if isinstance(user_stmt, tree.Import) and not is_completion:
|
return evaluator.eval_element(eval_stmt)
|
||||||
i, _ = helpers.get_on_import_stmt(evaluator, user_context,
|
|
||||||
user_stmt, is_completion)
|
|
||||||
if i is None:
|
|
||||||
return []
|
|
||||||
scopes = [i]
|
|
||||||
else:
|
|
||||||
# Just parse one statement, take it and evaluate it.
|
|
||||||
eval_stmt = get_under_cursor_stmt(evaluator, parser, dotted_path, position)
|
|
||||||
if eval_stmt is None:
|
|
||||||
return []
|
|
||||||
|
|
||||||
if not is_completion:
|
|
||||||
module = evaluator.wrap(parser.module())
|
|
||||||
names, level, _, _ = helpers.check_error_statements(module, position)
|
|
||||||
if names:
|
|
||||||
names = [str(n) for n in names]
|
|
||||||
i = imports.Importer(evaluator, names, module, level)
|
|
||||||
return i.follow()
|
|
||||||
|
|
||||||
scopes = evaluator.eval_element(eval_stmt)
|
|
||||||
|
|
||||||
return scopes
|
|
||||||
|
|
||||||
|
|
||||||
@memoize_default(evaluator_is_first_arg=True)
|
@memoize_default(evaluator_is_first_arg=True)
|
||||||
|
|||||||
@@ -441,16 +441,19 @@ class Evaluator(object):
|
|||||||
def goto_definition(self, name):
|
def goto_definition(self, name):
|
||||||
# TODO rename to goto_definitions
|
# TODO rename to goto_definitions
|
||||||
def_ = name.get_definition()
|
def_ = name.get_definition()
|
||||||
if def_.type == 'expr_stmt' and name in def_.get_defined_names():
|
is_simple_name = name.parent.type not in ('power', 'trailer')
|
||||||
types = self.eval_statement(def_, name)
|
if is_simple_name:
|
||||||
elif def_.type == 'for_stmt':
|
if def_.type == 'expr_stmt' and name in def_.get_defined_names():
|
||||||
container_types = self.eval_element(def_.children[3])
|
return self.eval_statement(def_, name)
|
||||||
for_types = iterable.py__iter__types(self, container_types, def_.children[3])
|
elif def_.type == 'for_stmt':
|
||||||
types = finder.check_tuple_assignments(self, for_types, name)
|
container_types = self.eval_element(def_.children[3])
|
||||||
else:
|
for_types = iterable.py__iter__types(self, container_types, def_.children[3])
|
||||||
call = helpers.call_of_name(name)
|
return finder.check_tuple_assignments(self, for_types, name)
|
||||||
types = self.eval_element(call)
|
elif def_.type == 'import_from':
|
||||||
return types
|
return imports.ImportWrapper(self, name).follow()
|
||||||
|
|
||||||
|
call = helpers.call_of_name(name)
|
||||||
|
return self.eval_element(call)
|
||||||
|
|
||||||
def goto(self, name):
|
def goto(self, name):
|
||||||
def resolve_implicit_imports(names):
|
def resolve_implicit_imports(names):
|
||||||
|
|||||||
@@ -560,7 +560,7 @@ class BaseNode(Base):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
return c
|
return c
|
||||||
|
|
||||||
raise ValueError("Position does not exist.")
|
return None
|
||||||
|
|
||||||
@Python3Method
|
@Python3Method
|
||||||
def get_statement_for_position(self, pos):
|
def get_statement_for_position(self, pos):
|
||||||
|
|||||||
@@ -125,6 +125,9 @@ f
|
|||||||
# -----------------
|
# -----------------
|
||||||
# unnessecary braces
|
# unnessecary braces
|
||||||
# -----------------
|
# -----------------
|
||||||
|
a = (1)
|
||||||
|
#? int()
|
||||||
|
a
|
||||||
#? int()
|
#? int()
|
||||||
(1)
|
(1)
|
||||||
#? int()
|
#? int()
|
||||||
|
|||||||
@@ -131,6 +131,8 @@ A().addition
|
|||||||
A().addition = None
|
A().addition = None
|
||||||
#? 8 int()
|
#? 8 int()
|
||||||
A(1).addition = None
|
A(1).addition = None
|
||||||
|
#? 1 A
|
||||||
|
A(1).addition = None
|
||||||
a = A()
|
a = A()
|
||||||
#? 8 int()
|
#? 8 int()
|
||||||
a.addition = None
|
a.addition = None
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ Fallback to callee definition when definition not found.
|
|||||||
|
|
||||||
"""Parenthesis closed at next line."""
|
"""Parenthesis closed at next line."""
|
||||||
|
|
||||||
|
# Ignore these definitions for a little while, not sure if we really want them.
|
||||||
|
# python <= 2.7
|
||||||
|
|
||||||
#? isinstance
|
#? isinstance
|
||||||
isinstance(
|
isinstance(
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -202,8 +202,9 @@ default_function()
|
|||||||
def a():
|
def a():
|
||||||
l = 3
|
l = 3
|
||||||
def func_b():
|
def func_b():
|
||||||
#? str()
|
|
||||||
l = ''
|
l = ''
|
||||||
|
#? str()
|
||||||
|
l
|
||||||
#? ['func_b']
|
#? ['func_b']
|
||||||
func_b
|
func_b
|
||||||
#? int()
|
#? int()
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ def scope_from_import_variable():
|
|||||||
without the use of ``sys.modules`` modifications (e.g. ``os.path`` see also
|
without the use of ``sys.modules`` modifications (e.g. ``os.path`` see also
|
||||||
github issue #213 for clarification.
|
github issue #213 for clarification.
|
||||||
"""
|
"""
|
||||||
|
a = 3
|
||||||
#?
|
#?
|
||||||
from import_tree.mod2.fake import a
|
from import_tree.mod2.fake import a
|
||||||
#?
|
#?
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ if isi
|
|||||||
try:
|
try:
|
||||||
except TypeError:
|
except TypeError:
|
||||||
#? str()
|
#? str()
|
||||||
""
|
str()
|
||||||
|
|
||||||
def break(): pass
|
def break(): pass
|
||||||
# wrong ternary expression
|
# wrong ternary expression
|
||||||
|
|||||||
Reference in New Issue
Block a user