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`
|
||||
"""
|
||||
def resolve_import_paths(definitions):
|
||||
new_defs = list(definitions)
|
||||
for s in definitions:
|
||||
if isinstance(s, imports.ImportWrapper):
|
||||
new_defs.remove(s)
|
||||
new_defs += resolve_import_paths(set(s.follow()))
|
||||
return new_defs
|
||||
c = helpers.ContextResults(self._evaluator, self.source, self._get_module(), self._pos)
|
||||
definitions = c.get_results()
|
||||
|
||||
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]
|
||||
defs = [classes.Definition(self._evaluator, name) for name in names]
|
||||
# The additional set here allows the definitions to become unique in an
|
||||
|
||||
@@ -183,7 +183,7 @@ class Completion:
|
||||
else:
|
||||
scopes = list(inference.type_inference(
|
||||
self._evaluator, self._parser, self._user_context,
|
||||
self._pos, completion_parts.path, is_completion=True
|
||||
self._pos, completion_parts.path
|
||||
))
|
||||
completion_names = []
|
||||
debug.dbg('possible completion scopes: %s', scopes)
|
||||
|
||||
@@ -212,3 +212,70 @@ def importer_from_error_statement(error_statement, pos):
|
||||
only_modules = False
|
||||
|
||||
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.
|
||||
"""
|
||||
from jedi import debug
|
||||
from jedi.api import helpers
|
||||
from jedi.parser import tree
|
||||
from jedi.parser import Parser, ParseError
|
||||
from jedi.evaluate import imports
|
||||
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
|
||||
under cursor.
|
||||
"""
|
||||
debug.dbg('start: %s in %s', dotted_path, parser.user_scope())
|
||||
|
||||
user_stmt = parser.user_stmt_with_whitespace()
|
||||
if not user_stmt and len(dotted_path.split('\n')) > 1:
|
||||
# If the user_stmt is not defined and the dotted_path is multi line,
|
||||
# something's strange. Most probably the backwards tokenizer
|
||||
# matched to much.
|
||||
# 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 isinstance(user_stmt, tree.Import) and not is_completion:
|
||||
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
|
||||
return evaluator.eval_element(eval_stmt)
|
||||
|
||||
|
||||
@memoize_default(evaluator_is_first_arg=True)
|
||||
|
||||
@@ -441,16 +441,19 @@ class Evaluator(object):
|
||||
def goto_definition(self, name):
|
||||
# TODO rename to goto_definitions
|
||||
def_ = name.get_definition()
|
||||
if def_.type == 'expr_stmt' and name in def_.get_defined_names():
|
||||
types = self.eval_statement(def_, name)
|
||||
elif def_.type == 'for_stmt':
|
||||
container_types = self.eval_element(def_.children[3])
|
||||
for_types = iterable.py__iter__types(self, container_types, def_.children[3])
|
||||
types = finder.check_tuple_assignments(self, for_types, name)
|
||||
else:
|
||||
call = helpers.call_of_name(name)
|
||||
types = self.eval_element(call)
|
||||
return types
|
||||
is_simple_name = name.parent.type not in ('power', 'trailer')
|
||||
if is_simple_name:
|
||||
if def_.type == 'expr_stmt' and name in def_.get_defined_names():
|
||||
return self.eval_statement(def_, name)
|
||||
elif def_.type == 'for_stmt':
|
||||
container_types = self.eval_element(def_.children[3])
|
||||
for_types = iterable.py__iter__types(self, container_types, def_.children[3])
|
||||
return finder.check_tuple_assignments(self, for_types, name)
|
||||
elif def_.type == 'import_from':
|
||||
return imports.ImportWrapper(self, name).follow()
|
||||
|
||||
call = helpers.call_of_name(name)
|
||||
return self.eval_element(call)
|
||||
|
||||
def goto(self, name):
|
||||
def resolve_implicit_imports(names):
|
||||
|
||||
@@ -560,7 +560,7 @@ class BaseNode(Base):
|
||||
except AttributeError:
|
||||
return c
|
||||
|
||||
raise ValueError("Position does not exist.")
|
||||
return None
|
||||
|
||||
@Python3Method
|
||||
def get_statement_for_position(self, pos):
|
||||
|
||||
@@ -125,6 +125,9 @@ f
|
||||
# -----------------
|
||||
# unnessecary braces
|
||||
# -----------------
|
||||
a = (1)
|
||||
#? int()
|
||||
a
|
||||
#? int()
|
||||
(1)
|
||||
#? int()
|
||||
|
||||
@@ -131,6 +131,8 @@ A().addition
|
||||
A().addition = None
|
||||
#? 8 int()
|
||||
A(1).addition = None
|
||||
#? 1 A
|
||||
A(1).addition = None
|
||||
a = A()
|
||||
#? 8 int()
|
||||
a.addition = None
|
||||
|
||||
@@ -6,6 +6,9 @@ Fallback to callee definition when definition not found.
|
||||
|
||||
"""Parenthesis closed at next line."""
|
||||
|
||||
# Ignore these definitions for a little while, not sure if we really want them.
|
||||
# python <= 2.7
|
||||
|
||||
#? isinstance
|
||||
isinstance(
|
||||
)
|
||||
|
||||
@@ -202,8 +202,9 @@ default_function()
|
||||
def a():
|
||||
l = 3
|
||||
def func_b():
|
||||
#? str()
|
||||
l = ''
|
||||
#? str()
|
||||
l
|
||||
#? ['func_b']
|
||||
func_b
|
||||
#? int()
|
||||
|
||||
@@ -75,6 +75,7 @@ def scope_from_import_variable():
|
||||
without the use of ``sys.modules`` modifications (e.g. ``os.path`` see also
|
||||
github issue #213 for clarification.
|
||||
"""
|
||||
a = 3
|
||||
#?
|
||||
from import_tree.mod2.fake import a
|
||||
#?
|
||||
|
||||
@@ -102,7 +102,7 @@ if isi
|
||||
try:
|
||||
except TypeError:
|
||||
#? str()
|
||||
""
|
||||
str()
|
||||
|
||||
def break(): pass
|
||||
# wrong ternary expression
|
||||
|
||||
Reference in New Issue
Block a user