mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-16 10:37:52 +08:00
A lot of call signature refactorings. Note that this commit is totally broken.
This commit is contained in:
@@ -182,8 +182,12 @@ class Script(object):
|
|||||||
|
|
||||||
:rtype: list of :class:`classes.Definition`
|
:rtype: list of :class:`classes.Definition`
|
||||||
"""
|
"""
|
||||||
c = helpers.ContextResults(self._evaluator, self.source, self._get_module(), self._pos)
|
leaf = self._get_module().name_for_position(self._pos)
|
||||||
definitions = c.get_results()
|
if leaf is None:
|
||||||
|
leaf = self._get_module().get_leaf_for_position(self._pos)
|
||||||
|
if leaf is None:
|
||||||
|
return []
|
||||||
|
definitions = helpers.evaluate_goto_definition(self._evaluator, leaf)
|
||||||
|
|
||||||
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]
|
||||||
@@ -272,26 +276,30 @@ class Script(object):
|
|||||||
|
|
||||||
abs()# <-- cursor is here
|
abs()# <-- cursor is here
|
||||||
|
|
||||||
This would return ``None``.
|
This would return an empty list..
|
||||||
|
|
||||||
:rtype: list of :class:`classes.CallSignature`
|
:rtype: list of :class:`classes.CallSignature`
|
||||||
"""
|
"""
|
||||||
call_txt, call_index, key_name, start_pos = self._user_context.call_signature()
|
call_signature_details = \
|
||||||
if call_txt is None:
|
helpers.get_call_signature_details(self._get_module(), self._pos)
|
||||||
|
if call_signature_details is None:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
stmt = inference.get_under_cursor_stmt(self._evaluator, self._parser,
|
# TODO insert caching again here.
|
||||||
call_txt, start_pos)
|
#with common.scale_speed_settings(settings.scale_call_signatures):
|
||||||
if stmt is None:
|
# definitions = cache.cache_call_signatures(self._evaluator, stmt,
|
||||||
return []
|
# self.source, self._pos)
|
||||||
|
definitions = helpers.evaluate_goto_definition(
|
||||||
with common.scale_speed_settings(settings.scale_call_signatures):
|
self._evaluator,
|
||||||
origins = cache.cache_call_signatures(self._evaluator, stmt,
|
call_signature_details.leaf
|
||||||
self.source, self._pos)
|
)
|
||||||
debug.speed('func_call followed')
|
debug.speed('func_call followed')
|
||||||
|
|
||||||
return [classes.CallSignature(self._evaluator, o.name, stmt, call_index, key_name)
|
return [classes.CallSignature(self._evaluator, d.name,
|
||||||
for o in origins if hasattr(o, 'py__call__')]
|
call_signature_details.leaf.start_pos,
|
||||||
|
call_signature_details.call_index,
|
||||||
|
call_signature_details.keyword_name)
|
||||||
|
for d in definitions if hasattr(d, 'py__call__')]
|
||||||
|
|
||||||
def _analysis(self):
|
def _analysis(self):
|
||||||
self._evaluator.is_analysis = True
|
self._evaluator.is_analysis = True
|
||||||
|
|||||||
@@ -629,11 +629,11 @@ class CallSignature(Definition):
|
|||||||
It knows what functions you are currently in. e.g. `isinstance(` would
|
It knows what functions you are currently in. e.g. `isinstance(` would
|
||||||
return the `isinstance` function. without `(` it would return nothing.
|
return the `isinstance` function. without `(` it would return nothing.
|
||||||
"""
|
"""
|
||||||
def __init__(self, evaluator, executable_name, call_stmt, index, key_name):
|
def __init__(self, evaluator, executable_name, bracket_start_pos, index, key_name):
|
||||||
super(CallSignature, self).__init__(evaluator, executable_name)
|
super(CallSignature, self).__init__(evaluator, executable_name)
|
||||||
self._index = index
|
self._index = index
|
||||||
self._key_name = key_name
|
self._key_name = key_name
|
||||||
self._call_stmt = call_stmt
|
self._bracket_start_pos = bracket_start_pos
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def index(self):
|
def index(self):
|
||||||
@@ -665,7 +665,7 @@ class CallSignature(Definition):
|
|||||||
The indent of the bracket that is responsible for the last function
|
The indent of the bracket that is responsible for the last function
|
||||||
call.
|
call.
|
||||||
"""
|
"""
|
||||||
return self._call_stmt.end_pos
|
return self._bracket_start_pos
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def call_name(self):
|
def call_name(self):
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from collections import namedtuple
|
|||||||
|
|
||||||
from jedi import common
|
from jedi import common
|
||||||
from jedi.evaluate import imports
|
from jedi.evaluate import imports
|
||||||
|
from jedi.evaluate.helpers import deep_ast_copy
|
||||||
from jedi import parser
|
from jedi import parser
|
||||||
from jedi.parser import tokenize, token
|
from jedi.parser import tokenize, token
|
||||||
|
|
||||||
@@ -169,68 +170,63 @@ def get_possible_completion_types(grammar, stack):
|
|||||||
return keywords, grammar_labels
|
return keywords, grammar_labels
|
||||||
|
|
||||||
|
|
||||||
class ContextResults():
|
def evaluate_goto_definition(evaluator, leaf):
|
||||||
def __init__(self, evaluator, source, module, pos):
|
if leaf.type == 'name':
|
||||||
self._evaluator = evaluator
|
# In case of a name we can just use goto_definition which does all the
|
||||||
self._module = module
|
# magic itself.
|
||||||
self._source = source
|
return evaluator.goto_definitions(leaf)
|
||||||
self._pos = pos
|
|
||||||
|
|
||||||
def _on_defining_name(self, leaf):
|
node = None
|
||||||
return [self._evaluator.wrap(self._parser.user_scope())]
|
parent = leaf.parent
|
||||||
|
if parent.type == 'atom':
|
||||||
|
node = leaf.parent
|
||||||
|
elif parent.type == 'trailer':
|
||||||
|
index = parent.parent.children.index(parent)
|
||||||
|
node = deep_ast_copy(parent.parent)
|
||||||
|
node.children = node.children[:index + 1]
|
||||||
|
|
||||||
def get_results(self):
|
if node is None:
|
||||||
'''
|
|
||||||
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_definitions(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 []
|
return []
|
||||||
symbol_names = list(stack.get_node_names(self._evaluator.grammar))
|
return evaluator.eval_element(node)
|
||||||
|
|
||||||
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):
|
CallSignatureDetails = namedtuple(
|
||||||
def _():
|
'CallSignatureDetails',
|
||||||
definitions = inference.type_inference(
|
['leaf', 'call_index', 'keyword_name']
|
||||||
self._evaluator, self._parser, self._user_context,
|
)
|
||||||
self._pos, goto_path
|
|
||||||
)
|
|
||||||
|
def _get_call_signature_details_from_error_node(node, position):
|
||||||
|
for index, element in reversed(list(enumerate(node.children))):
|
||||||
|
# `index > 0` means that it's a trailer and not an atom.
|
||||||
|
if element == '(' and element.end_pos <= position and index > 0:
|
||||||
|
name = element.get_previous_leaf()
|
||||||
|
if name.type == 'name':
|
||||||
|
nodes_before = [c for c in node.children[index:] if c.start_pos < position]
|
||||||
|
return CallSignatureDetails(name, nodes_before.count(','), None)
|
||||||
|
|
||||||
|
|
||||||
|
def get_call_signature_details(module, position):
|
||||||
|
leaf = module.get_leaf_for_position(position, include_prefixes=True)
|
||||||
|
if leaf == ')':
|
||||||
|
if leaf.end_pos == position:
|
||||||
|
leaf = leaf.get_next_leaf()
|
||||||
|
# Now that we know where we are in the syntax tree, we start to look at
|
||||||
|
# parents for possible function definitions.
|
||||||
|
node = leaf.parent
|
||||||
|
name = None
|
||||||
|
while node is not None:
|
||||||
|
for n in node.children:
|
||||||
|
if n.start_pos < position and n.type == 'error_node':
|
||||||
|
result = _get_call_signature_details_from_error_node(n, position)
|
||||||
|
if result is not None:
|
||||||
|
return result
|
||||||
|
|
||||||
|
if node.type == 'trailer' and node.children[0] == '(':
|
||||||
|
name = node.get_previous_sibling()
|
||||||
|
nodes_before = [c for c in node.children if c.start_pos < position]
|
||||||
|
return CallSignatureDetails(name, nodes_before.count(','), None)
|
||||||
|
|
||||||
|
node = node.parent
|
||||||
|
|
||||||
|
return None
|
||||||
|
|||||||
@@ -81,15 +81,32 @@ def call_of_name(name, cut_own_trailer=False):
|
|||||||
# TODO remove cut_own_trailer option, since its always used with it. Just
|
# TODO remove cut_own_trailer option, since its always used with it. Just
|
||||||
# ignore it, It's not what we want anyway. Or document it better?
|
# ignore it, It's not what we want anyway. Or document it better?
|
||||||
"""
|
"""
|
||||||
par = name
|
trailer = name.parent
|
||||||
if tree.is_node(par.parent, 'trailer'):
|
if trailer.type != 'trailer' or trailer.children[0] != '.':
|
||||||
|
return name
|
||||||
|
|
||||||
|
assert not cut_own_trailer # TODO remove
|
||||||
|
power = trailer.parent
|
||||||
|
index = power.children.index(trailer)
|
||||||
|
power = deep_ast_copy(power)
|
||||||
|
power.children[index + 1:] = []
|
||||||
|
|
||||||
|
if power.type == 'error_node':
|
||||||
|
transformed = tree.Node('power', power.children)
|
||||||
|
transformed.parent = power.parent
|
||||||
|
return transformed
|
||||||
|
|
||||||
|
return power
|
||||||
|
if 1:
|
||||||
par = par.parent
|
par = par.parent
|
||||||
if par.children[0] in ('(', '['):
|
if par.children[0] in ('(', '['):
|
||||||
# The trailer is not a NAME.NAME trailer, but a call to something.
|
# The trailer is not a NAME.NAME trailer, but a call to something.
|
||||||
return name
|
return name
|
||||||
|
|
||||||
power = par.parent
|
power = par.parent
|
||||||
if tree.is_node(power, 'power', 'atom_expr') \
|
# `atom_expr` got introduced in Python 3.5 and is essentially just the
|
||||||
|
# whole call part without the optional ** power element.
|
||||||
|
if power.type in ('power', 'atom_expr') \
|
||||||
and power.children[0] != name \
|
and power.children[0] != name \
|
||||||
and not (power.children[-2] == '**' and
|
and not (power.children[-2] == '**' and
|
||||||
name.start_pos > power.children[-1].start_pos):
|
name.start_pos > power.children[-1].start_pos):
|
||||||
|
|||||||
@@ -83,6 +83,9 @@ TestClass.var_local.
|
|||||||
|
|
||||||
#? int()
|
#? int()
|
||||||
TestClass().ret(1)
|
TestClass().ret(1)
|
||||||
|
# Should not return int(), because we want the type before `.ret(1)`.
|
||||||
|
#? 11 TestClass()
|
||||||
|
TestClass().ret(1)
|
||||||
#? int()
|
#? int()
|
||||||
inst.ret(1)
|
inst.ret(1)
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ class TestCallSignatures(TestCase):
|
|||||||
assert len(signatures) <= 1
|
assert len(signatures) <= 1
|
||||||
|
|
||||||
if not signatures:
|
if not signatures:
|
||||||
assert expected_name is None
|
assert expected_name is None, \
|
||||||
|
'There are no signatures, but %s expected.' % expected_name
|
||||||
else:
|
else:
|
||||||
assert signatures[0].name == expected_name
|
assert signatures[0].name == expected_name
|
||||||
assert signatures[0].index == expected_index
|
assert signatures[0].index == expected_index
|
||||||
@@ -27,9 +28,6 @@ class TestCallSignatures(TestCase):
|
|||||||
|
|
||||||
def test_simple(self):
|
def test_simple(self):
|
||||||
run = self._run_simple
|
run = self._run_simple
|
||||||
s7 = "str().upper().center("
|
|
||||||
s8 = "str(int[zip("
|
|
||||||
run(s7, 'center', 0)
|
|
||||||
|
|
||||||
# simple
|
# simple
|
||||||
s1 = "sorted(a, str("
|
s1 = "sorted(a, str("
|
||||||
|
|||||||
Reference in New Issue
Block a user