follow_statement -> eval_statement

This commit is contained in:
Dave Halter
2013-12-27 11:55:35 +01:00
parent b7958b32a3
commit eb30c3e6cf
7 changed files with 31 additions and 29 deletions

View File

@@ -242,7 +242,7 @@ class Script(object):
else: else:
# just parse one statement, take it and evaluate it # just parse one statement, take it and evaluate it
stmt = self._get_under_cursor_stmt(goto_path) stmt = self._get_under_cursor_stmt(goto_path)
scopes = self._evaluator.follow_statement(stmt) scopes = self._evaluator.eval_statement(stmt)
return scopes return scopes
def _get_under_cursor_stmt(self, cursor_txt): def _get_under_cursor_stmt(self, cursor_txt):

View File

@@ -401,7 +401,7 @@ class Completion(BaseDefinition):
""" """
if self._followed_definitions is None: if self._followed_definitions is None:
if self._definition.isinstance(pr.Statement): if self._definition.isinstance(pr.Statement):
defs = self._evaluator.follow_statement(self._definition) defs = self._evaluator.eval_statement(self._definition)
elif self._definition.isinstance(pr.Import): elif self._definition.isinstance(pr.Import):
defs = imports.strip_imports(self._evaluator, [self._definition]) defs = imports.strip_imports(self._evaluator, [self._definition])
else: else:

View File

@@ -52,7 +52,7 @@ def follow_param(evaluator, param):
p = Parser(param_str, None, user_position, no_docstr=True) p = Parser(param_str, None, user_position, no_docstr=True)
if p.user_stmt is None: if p.user_stmt is None:
return [] return []
return evaluator.follow_statement(p.user_stmt) return evaluator.eval_statement(p.user_stmt)
return [] return []
@@ -127,4 +127,4 @@ def find_return_types(evaluator, func):
if p.user_stmt is None: if p.user_stmt is None:
return [] return []
p.user_stmt.parent = func p.user_stmt.parent = func
return list(evaluator.follow_statement(p.user_stmt)) return list(evaluator.eval_statement(p.user_stmt))

View File

@@ -11,23 +11,23 @@ Evaluation of Python code in |jedi| is based on three assumptions:
* The programmer is not a total dick, e.g. like `this * The programmer is not a total dick, e.g. like `this
<https://github.com/davidhalter/jedi/issues/24>`_ :-) <https://github.com/davidhalter/jedi/issues/24>`_ :-)
That said, there's mainly one entry point in this script: ``follow_statement``. That said, there's mainly one entry point in this script: ``eval_statement``.
This is where autocompletion starts. Everything you want to complete is either This is where autocompletion starts. Everything you want to complete is either
a ``Statement`` or some special name like ``class``, which is easy to complete. a ``Statement`` or some special name like ``class``, which is easy to complete.
Therefore you need to understand what follows after ``follow_statement``. Let's Therefore you need to understand what follows after ``eval_statement``. Let's
make an example:: make an example::
import datetime import datetime
datetime.date.toda# <-- cursor here datetime.date.toda# <-- cursor here
First of all, this module doesn't care about completion. It really just cares First of all, this module doesn't care about completion. It really just cares
about ``datetime.date``. At the end of the procedure ``follow_statement`` will about ``datetime.date``. At the end of the procedure ``eval_statement`` will
return the ``datetime`` class. return the ``datetime`` class.
To *visualize* this (simplified): To *visualize* this (simplified):
- ``follow_statement`` - ``<Statement: datetime.date>`` - ``eval_statement`` - ``<Statement: datetime.date>``
- Unpacking of the statement into ``[[<Call: datetime.date>]]`` - Unpacking of the statement into ``[[<Call: datetime.date>]]``
- ``follow_call_list``, calls ``follow_call`` with ``<Call: datetime.date>`` - ``follow_call_list``, calls ``follow_call`` with ``<Call: datetime.date>``
@@ -50,7 +50,7 @@ import would contain another Statement like this::
from foo import bar from foo import bar
Date = bar.baz Date = bar.baz
Well... You get it. Just another ``follow_statement`` recursion. It's really Well... You get it. Just another ``eval_statement`` recursion. It's really
easy. Just that Python is not that easy sometimes. To understand tuple easy. Just that Python is not that easy sometimes. To understand tuple
assignments and different class scopes, a lot more code had to be written. Yet assignments and different class scopes, a lot more code had to be written. Yet
we're still not talking about Descriptors and Nested List Comprehensions, just we're still not talking about Descriptors and Nested List Comprehensions, just
@@ -280,7 +280,7 @@ class Evaluator(object):
#if r.docstr: #if r.docstr:
#res_new.append(r) #res_new.append(r)
scopes = self.follow_statement(r, seek_name=name_str) scopes = self.eval_statement(r, seek_name=name_str)
add += remove_statements(scopes) add += remove_statements(scopes)
if check_instance is not None: if check_instance is not None:
@@ -310,7 +310,7 @@ class Evaluator(object):
# one, remember `in`). And follow it. # one, remember `in`). And follow it.
if not loop.inputs: if not loop.inputs:
return [] return []
result = get_iterator_types(self.follow_statement(loop.inputs[0])) result = get_iterator_types(self.eval_statement(loop.inputs[0]))
if len(loop.set_vars) > 1: if len(loop.set_vars) > 1:
commands = loop.set_stmt.get_commands() commands = loop.set_stmt.get_commands()
# loops with loop.set_vars > 0 only have one command # loops with loop.set_vars > 0 only have one command
@@ -478,7 +478,7 @@ class Evaluator(object):
@memoize_default(default=(), evaluator_is_first_arg=True) @memoize_default(default=(), evaluator_is_first_arg=True)
@recursion.recursion_decorator @recursion.recursion_decorator
def follow_statement(self, stmt, seek_name=None): def eval_statement(self, stmt, seek_name=None):
""" """
The starting point of the completion. A statement always owns a call list, The starting point of the completion. A statement always owns a call list,
which are the calls, that a statement does. which are the calls, that a statement does.
@@ -488,9 +488,8 @@ class Evaluator(object):
:param stmt: A `pr.Statement`. :param stmt: A `pr.Statement`.
:param seek_name: A string. :param seek_name: A string.
""" """
debug.dbg('follow_stmt %s (%s)' % (stmt, seek_name)) debug.dbg('eval_statement %s (%s)' % (stmt, seek_name))
commands = stmt.get_commands() commands = stmt.get_commands()
debug.dbg('calls: %s' % commands)
result = self.follow_call_list(commands) result = self.follow_call_list(commands)
@@ -526,11 +525,12 @@ class Evaluator(object):
loop = evaluate_list_comprehension(nested_lc, loop) loop = evaluate_list_comprehension(nested_lc, loop)
return loop return loop
debug.dbg('follow_call_list: %s' % call_list)
result = [] result = []
calls_iterator = iter(call_list) calls_iterator = iter(call_list)
for call in calls_iterator: for call in calls_iterator:
if pr.Array.is_type(call, pr.Array.NOARRAY): if pr.Array.is_type(call, pr.Array.NOARRAY):
r = list(itertools.chain.from_iterable(self.follow_statement(s) r = list(itertools.chain.from_iterable(self.eval_statement(s)
for s in call)) for s in call))
call_path = call.generate_call_path() call_path = call.generate_call_path()
next(call_path, None) # the first one has been used already next(call_path, None) # the first one has been used already
@@ -541,7 +541,7 @@ class Evaluator(object):
# Caveat: parents are being changed, but this doesn't matter, # Caveat: parents are being changed, but this doesn't matter,
# because nothing else uses it. # because nothing else uses it.
call.stmt.parent = loop call.stmt.parent = loop
result += self.follow_statement(call.stmt) result += self.eval_statement(call.stmt)
else: else:
if isinstance(call, pr.Lambda): if isinstance(call, pr.Lambda):
result.append(er.Function(self, call)) result.append(er.Function(self, call))
@@ -582,7 +582,9 @@ class Evaluator(object):
return self.follow_call_path(path, s.parent, s.start_pos) return self.follow_call_path(path, s.parent, s.start_pos)
def follow_call_path(self, path, scope, position): def follow_call_path(self, path, scope, position):
"""Follows a path generated by `pr.StatementElement.generate_call_path()`""" """
Follows a path generated by `pr.StatementElement.generate_call_path()`.
"""
current = next(path) current = next(path)
if isinstance(current, pr.Array): if isinstance(current, pr.Array):
@@ -635,7 +637,7 @@ class Evaluator(object):
current = next(path) current = next(path)
except StopIteration: except StopIteration:
return None return None
debug.dbg('follow %s in scope %s' % (current, scope)) debug.dbg('follow_path: %s in scope %s' % (current, scope))
result = [] result = []
if isinstance(current, pr.Array): if isinstance(current, pr.Array):

View File

@@ -195,7 +195,7 @@ def search_params(evaluator, param):
for params in get_posibilities(evaluator, module, func_name): for params in get_posibilities(evaluator, module, func_name):
for p in params: for p in params:
if str(p) == param_name: if str(p) == param_name:
result += evaluator.follow_statement(p.parent) result += evaluator.eval_statement(p.parent)
return result return result
func = param.get_parent_until(pr.Function) func = param.get_parent_until(pr.Function)
@@ -317,17 +317,17 @@ def _check_array_additions(evaluator, compare_array, module, is_list):
continue # no params: just ignore it continue # no params: just ignore it
if add_name in ['append', 'add']: if add_name in ['append', 'add']:
for param in params: for param in params:
result += evaluator.follow_statement(param) result += evaluator.eval_statement(param)
elif add_name in ['insert']: elif add_name in ['insert']:
try: try:
second_param = params[1] second_param = params[1]
except IndexError: except IndexError:
continue continue
else: else:
result += evaluator.follow_statement(second_param) result += evaluator.eval_statement(second_param)
elif add_name in ['extend', 'update']: elif add_name in ['extend', 'update']:
for param in params: for param in params:
iterators = evaluator.follow_statement(param) iterators = evaluator.eval_statement(param)
result += evaluate.get_iterator_types(iterators) result += evaluate.get_iterator_types(iterators)
return result return result
@@ -411,7 +411,7 @@ class ArrayInstance(pr.Base):
items = [] items = []
from jedi import evaluate from jedi import evaluate
for stmt in self.var_args: for stmt in self.var_args:
for typ in self._evaluator.follow_statement(stmt): for typ in self._evaluator.eval_statement(stmt):
if isinstance(typ, evaluate.er.Instance) and len(typ.var_args): if isinstance(typ, evaluate.er.Instance) and len(typ.var_args):
array = typ.var_args[0] array = typ.var_args[0]
if isinstance(array, ArrayInstance): if isinstance(array, ArrayInstance):

View File

@@ -262,7 +262,7 @@ class Class(use_metaclass(CachedMetaClass, pr.IsScope)):
# TODO care for mro stuff (multiple super classes). # TODO care for mro stuff (multiple super classes).
for s in self.base.supers: for s in self.base.supers:
# Super classes are statements. # Super classes are statements.
for cls in self._evaluator.follow_statement(s): for cls in self._evaluator.eval_statement(s):
if not isinstance(cls, Class): if not isinstance(cls, Class):
debug.warning('Received non class, as a super class') debug.warning('Received non class, as a super class')
continue # Just ignore other stuff (user input error). continue # Just ignore other stuff (user input error).
@@ -343,7 +343,7 @@ class Function(use_metaclass(CachedMetaClass, pr.IsScope)):
if not self.is_decorated: if not self.is_decorated:
for dec in reversed(self.base_func.decorators): for dec in reversed(self.base_func.decorators):
debug.dbg('decorator:', dec, f) debug.dbg('decorator:', dec, f)
dec_results = set(self._evaluator.follow_statement(dec)) dec_results = set(self._evaluator.eval_statement(dec))
if not len(dec_results): if not len(dec_results):
debug.warning('decorator not found: %s on %s' % debug.warning('decorator not found: %s on %s' %
(dec, self.base_func)) (dec, self.base_func))
@@ -416,7 +416,7 @@ class Execution(Executable):
return [] return []
else: else:
if isinstance(stmt, pr.Statement): if isinstance(stmt, pr.Statement):
return self._evaluator.follow_statement(stmt) return self._evaluator.eval_statement(stmt)
else: else:
return [stmt] # just some arbitrary object return [stmt] # just some arbitrary object
@@ -512,7 +512,7 @@ class Execution(Executable):
stmts = docstrings.find_return_types(self._evaluator, func) stmts = docstrings.find_return_types(self._evaluator, func)
for r in self.returns: for r in self.returns:
if r is not None: if r is not None:
stmts += self._evaluator.follow_statement(r) stmts += self._evaluator.eval_statement(r)
return stmts return stmts
@memoize_default(default=()) @memoize_default(default=())
@@ -889,7 +889,7 @@ class Array(use_metaclass(CachedMetaClass, pr.Base, Iterable)):
def _follow_values(self, values): def _follow_values(self, values):
""" helper function for the index getters """ """ helper function for the index getters """
return list(itertools.chain.from_iterable(self._evaluator.follow_statement(v) return list(itertools.chain.from_iterable(self._evaluator.eval_statement(v)
for v in values)) for v in values))
def get_defined_names(self): def get_defined_names(self):

View File

@@ -22,7 +22,7 @@ def test_complete_on_empty_import():
assert 10 < len(Script("from . import", 1, 5, '').completions()) < 30 assert 10 < len(Script("from . import", 1, 5, '').completions()) < 30
assert 10 < len(Script("from . import classes", 1, 5, '').completions()) < 30 assert 10 < len(Script("from . import classes", 1, 5, '').completions()) < 30
assert len(Script("import").completions()) == 0 assert len(Script("import").completions()) == 0
if not is_py26: if not is_py26: # python 2.6 doesn't always come with a library `import*`.
assert len(Script("import import", path='').completions()) > 0 assert len(Script("import import", path='').completions()) > 0
# 111 # 111