1
0
forked from VimPlug/jedi

A lot of call signature refactorings. Note that this commit is totally broken.

This commit is contained in:
Dave Halter
2016-06-17 00:20:13 +02:00
parent 6f366e2d77
commit 32346c6da8
6 changed files with 107 additions and 85 deletions

View File

@@ -182,8 +182,12 @@ class Script(object):
:rtype: list of :class:`classes.Definition`
"""
c = helpers.ContextResults(self._evaluator, self.source, self._get_module(), self._pos)
definitions = c.get_results()
leaf = self._get_module().name_for_position(self._pos)
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]
defs = [classes.Definition(self._evaluator, name) for name in names]
@@ -272,26 +276,30 @@ class Script(object):
abs()# <-- cursor is here
This would return ``None``.
This would return an empty list..
:rtype: list of :class:`classes.CallSignature`
"""
call_txt, call_index, key_name, start_pos = self._user_context.call_signature()
if call_txt is None:
call_signature_details = \
helpers.get_call_signature_details(self._get_module(), self._pos)
if call_signature_details is None:
return []
stmt = inference.get_under_cursor_stmt(self._evaluator, self._parser,
call_txt, start_pos)
if stmt is None:
return []
with common.scale_speed_settings(settings.scale_call_signatures):
origins = cache.cache_call_signatures(self._evaluator, stmt,
self.source, self._pos)
# TODO insert caching again here.
#with common.scale_speed_settings(settings.scale_call_signatures):
# definitions = cache.cache_call_signatures(self._evaluator, stmt,
# self.source, self._pos)
definitions = helpers.evaluate_goto_definition(
self._evaluator,
call_signature_details.leaf
)
debug.speed('func_call followed')
return [classes.CallSignature(self._evaluator, o.name, stmt, call_index, key_name)
for o in origins if hasattr(o, 'py__call__')]
return [classes.CallSignature(self._evaluator, d.name,
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):
self._evaluator.is_analysis = True

View File

@@ -629,11 +629,11 @@ class CallSignature(Definition):
It knows what functions you are currently in. e.g. `isinstance(` would
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)
self._index = index
self._key_name = key_name
self._call_stmt = call_stmt
self._bracket_start_pos = bracket_start_pos
@property
def index(self):
@@ -665,7 +665,7 @@ class CallSignature(Definition):
The indent of the bracket that is responsible for the last function
call.
"""
return self._call_stmt.end_pos
return self._bracket_start_pos
@property
def call_name(self):

View File

@@ -6,6 +6,7 @@ from collections import namedtuple
from jedi import common
from jedi.evaluate import imports
from jedi.evaluate.helpers import deep_ast_copy
from jedi import parser
from jedi.parser import tokenize, token
@@ -169,68 +170,63 @@ def get_possible_completion_types(grammar, stack):
return keywords, grammar_labels
class ContextResults():
def __init__(self, evaluator, source, module, pos):
self._evaluator = evaluator
self._module = module
self._source = source
self._pos = pos
def evaluate_goto_definition(evaluator, leaf):
if leaf.type == 'name':
# In case of a name we can just use goto_definition which does all the
# magic itself.
return evaluator.goto_definitions(leaf)
def _on_defining_name(self, leaf):
return [self._evaluator.wrap(self._parser.user_scope())]
node = None
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):
'''
try:
stack = get_stack_at_position(self._evaluator.grammar, self._source, self._module, self._leaf.end_pos)
except OnErrorLeaf:
if node is None:
return []
'''
return evaluator.eval_element(node)
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 []
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
CallSignatureDetails = namedtuple(
'CallSignatureDetails',
['leaf', 'call_index', 'keyword_name']
)
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
)
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

View File

@@ -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
# ignore it, It's not what we want anyway. Or document it better?
"""
par = name
if tree.is_node(par.parent, 'trailer'):
trailer = name.parent
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
if par.children[0] in ('(', '['):
# The trailer is not a NAME.NAME trailer, but a call to something.
return name
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 not (power.children[-2] == '**' and
name.start_pos > power.children[-1].start_pos):

View File

@@ -83,6 +83,9 @@ TestClass.var_local.
#? int()
TestClass().ret(1)
# Should not return int(), because we want the type before `.ret(1)`.
#? 11 TestClass()
TestClass().ret(1)
#? int()
inst.ret(1)

View File

@@ -14,7 +14,8 @@ class TestCallSignatures(TestCase):
assert len(signatures) <= 1
if not signatures:
assert expected_name is None
assert expected_name is None, \
'There are no signatures, but %s expected.' % expected_name
else:
assert signatures[0].name == expected_name
assert signatures[0].index == expected_index
@@ -27,9 +28,6 @@ class TestCallSignatures(TestCase):
def test_simple(self):
run = self._run_simple
s7 = "str().upper().center("
s8 = "str(int[zip("
run(s7, 'center', 0)
# simple
s1 = "sorted(a, str("