1
0
forked from VimPlug/jedi

create a customized UserContextParser for user_stmt and user_scope

This commit is contained in:
Dave Halter
2014-01-16 12:12:21 +01:00
parent 269e84da85
commit 4bf72eeaed
3 changed files with 61 additions and 46 deletions

View File

@@ -17,7 +17,7 @@ from jedi._compatibility import next, unicode, builtins
from jedi.parser import Parser from jedi.parser import Parser
from jedi.parser import representation as pr from jedi.parser import representation as pr
from jedi.parser import fast from jedi.parser import fast
from jedi.parser.user_context import UserContext from jedi.parser.user_context import UserContext, UserContextParser
from jedi import debug from jedi import debug
from jedi import settings from jedi import settings
from jedi import common from jedi import common
@@ -86,6 +86,7 @@ class Script(object):
debug.reset_time() debug.reset_time()
self.source = common.source_to_unicode(source, encoding) self.source = common.source_to_unicode(source, encoding)
self._user_context = UserContext(self.source, self._pos) self._user_context = UserContext(self.source, self._pos)
self._parser = UserContextParser(self.source, path, self._pos, self._user_context)
self._evaluator = Evaluator() self._evaluator = Evaluator()
debug.speed('init') debug.speed('init')
@@ -102,31 +103,6 @@ class Script(object):
def __repr__(self): def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__, repr(self._source_path)) return '<%s: %s>' % (self.__class__.__name__, repr(self._source_path))
@property
@cache.underscore_memoization
def _parser(self):
"""Get the parser lazy"""
path = self._source_path and os.path.abspath(self._source_path)
cache.invalidate_star_import_cache(path)
parser = fast.FastParser(self.source, path, self._pos)
# Don't pickle that module, because the main module is changing quickly
cache.save_parser(path, None, parser, pickling=False)
return parser
def _user_stmt(self, is_completion=False):
user_stmt = self._parser.user_stmt
debug.speed('parsed')
if is_completion and not user_stmt:
# for statements like `from x import ` (cursor not in statement)
pos = next(self._user_context.get_context(yield_positions=True))
last_stmt = pos and self._parser.module.get_statement_for_position(
pos, include_imports=True)
if isinstance(last_stmt, pr.Import):
user_stmt = last_stmt
return user_stmt
def completions(self): def completions(self):
""" """
Return :class:`classes.Completion` objects. Those objects contain Return :class:`classes.Completion` objects. Those objects contain
@@ -150,7 +126,7 @@ class Script(object):
return [] return []
path, dot, like = self._get_completion_parts() path, dot, like = self._get_completion_parts()
user_stmt = self._user_stmt(True) user_stmt = self._parser.user_stmt(True)
b = compiled.builtin b = compiled.builtin
completions = get_completions(user_stmt, b) completions = get_completions(user_stmt, b)
@@ -175,7 +151,7 @@ class Script(object):
and n.lower().startswith(like.lower()) \ and n.lower().startswith(like.lower()) \
or n.startswith(like): or n.startswith(like):
if not filter_private_variable(s, if not filter_private_variable(s,
user_stmt or self._parser.user_scope, n): user_stmt or self._parser.user_scope(), n):
new = classes.Completion(self._evaluator, c, needs_dot, len(like), s) new = classes.Completion(self._evaluator, c, needs_dot, len(like), s)
k = (new.name, new.complete) # key k = (new.name, new.complete) # key
if k in comp_dct and settings.no_completion_duplicates: if k in comp_dct and settings.no_completion_duplicates:
@@ -196,7 +172,7 @@ class Script(object):
except NotFoundError: except NotFoundError:
scopes = [] scopes = []
scope_generator = self._evaluator.get_names_of_scope( scope_generator = self._evaluator.get_names_of_scope(
self._parser.user_scope, self._pos) self._parser.user_scope(), self._pos)
completions = [] completions = []
for scope, name_list in scope_generator: for scope, name_list in scope_generator:
for c in name_list: for c in name_list:
@@ -230,9 +206,9 @@ class Script(object):
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', goto_path, self._parser.user_scope) debug.dbg('start: %s in %s', goto_path, self._parser.user_scope())
user_stmt = self._user_stmt(is_completion) user_stmt = self._parser.user_stmt(is_completion)
if not user_stmt and len(goto_path.split('\n')) > 1: if not user_stmt and len(goto_path.split('\n')) > 1:
# If the user_stmt is not defined and the goto_path is multi line, # If the user_stmt is not defined and the goto_path is multi line,
# something's strange. Most probably the backwards tokenizer # something's strange. Most probably the backwards tokenizer
@@ -254,7 +230,7 @@ class Script(object):
stmt = r.module.statements[0] stmt = r.module.statements[0]
except IndexError: except IndexError:
raise NotFoundError() raise NotFoundError()
stmt.parent = self._parser.user_scope stmt.parent = self._parser.user_scope()
return stmt return stmt
def complete(self): def complete(self):
@@ -346,7 +322,7 @@ class Script(object):
lower_priority_operators = ('()', '(', ',') lower_priority_operators = ('()', '(', ',')
"""Operators that could hide callee.""" """Operators that could hide callee."""
if next(context) in ('class', 'def'): if next(context) in ('class', 'def'):
scopes = set([self._parser.user_scope]) scopes = set([self._parser.user_scope()])
elif not goto_path: elif not goto_path:
op = self._user_context.get_operator_under_cursor() op = self._user_context.get_operator_under_cursor()
if op and op not in lower_priority_operators: if op and op not in lower_priority_operators:
@@ -416,9 +392,9 @@ class Script(object):
goto_path = self._user_context.get_path_under_cursor() goto_path = self._user_context.get_path_under_cursor()
context = self._user_context.get_context() context = self._user_context.get_context()
user_stmt = self._user_stmt() user_stmt = self._parser.user_stmt()
if next(context) in ('class', 'def'): if next(context) in ('class', 'def'):
user_scope = self._parser.user_scope user_scope = self._parser.user_scope()
definitions = set([user_scope.name]) definitions = set([user_scope.name])
search_name = unicode(user_scope.name) search_name = unicode(user_scope.name)
elif isinstance(user_stmt, pr.Import): elif isinstance(user_stmt, pr.Import):
@@ -462,7 +438,7 @@ class Script(object):
""" """
temp, settings.dynamic_flow_information = \ temp, settings.dynamic_flow_information = \
settings.dynamic_flow_information, False settings.dynamic_flow_information, False
user_stmt = self._user_stmt() user_stmt = self._parser.user_stmt()
definitions, search_name = self._goto(add_import_name=True) definitions, search_name = self._goto(add_import_name=True)
if isinstance(user_stmt, pr.Statement): if isinstance(user_stmt, pr.Statement):
c = user_stmt.expression_list()[0] c = user_stmt.expression_list()[0]
@@ -475,7 +451,7 @@ class Script(object):
definitions = usages_add_import_modules(self._evaluator, definitions, search_name) definitions = usages_add_import_modules(self._evaluator, definitions, search_name)
module = set([d.get_parent_until() for d in definitions]) module = set([d.get_parent_until() for d in definitions])
module.add(self._parser.module) module.add(self._parser.module())
names = usages(self._evaluator, definitions, search_name, module) names = usages(self._evaluator, definitions, search_name, module)
for d in set(definitions): for d in set(definitions):
@@ -512,7 +488,7 @@ class Script(object):
if call is None: if call is None:
return [] return []
user_stmt = self._user_stmt() user_stmt = self._parser.user_stmt()
with common.scale_speed_settings(settings.scale_call_signatures): with common.scale_speed_settings(settings.scale_call_signatures):
_callable = lambda: self._evaluator.eval_call(call) _callable = lambda: self._evaluator.eval_call(call)
origins = cache.cache_call_signatures(_callable, user_stmt) origins = cache.cache_call_signatures(_callable, user_stmt)
@@ -526,7 +502,7 @@ class Script(object):
debug.speed('func_call start') debug.speed('func_call start')
call, index = None, 0 call, index = None, 0
if call is None: if call is None:
user_stmt = self._user_stmt() user_stmt = self._parser.user_stmt()
if user_stmt is not None and isinstance(user_stmt, pr.Statement): if user_stmt is not None and isinstance(user_stmt, pr.Statement):
call, index, _ = helpers.search_call_signatures(user_stmt, self._pos) call, index, _ = helpers.search_call_signatures(user_stmt, self._pos)
debug.speed('func_call parsed') debug.speed('func_call parsed')
@@ -606,12 +582,12 @@ class Interpreter(Script):
self.namespaces = namespaces self.namespaces = namespaces
# Here we add the namespaces to the current parser. # Here we add the namespaces to the current parser.
importer = interpret.ObjectImporter(self._parser.user_scope) importer = interpret.ObjectImporter(self._parser.user_scope())
for ns in namespaces: for ns in namespaces:
importer.import_raw_namespace(ns) importer.import_raw_namespace(ns)
def _simple_complete(self, path, like): def _simple_complete(self, path, like):
user_stmt = self._user_stmt(True) user_stmt = self._parser.user_stmt(True)
is_simple_path = not path or re.search('^[\w][\w\d.]*$', path) is_simple_path = not path or re.search('^[\w][\w\d.]*$', path)
if isinstance(user_stmt, pr.Import) or not is_simple_path: if isinstance(user_stmt, pr.Import) or not is_simple_path:
return super(type(self), self)._simple_complete(path, like) return super(type(self), self)._simple_complete(path, like)
@@ -644,8 +620,8 @@ class Interpreter(Script):
for n in namespaces: for n in namespaces:
for name in dir(n): for name in dir(n):
if name.lower().startswith(like.lower()): if name.lower().startswith(like.lower()):
scope = self._parser.module scope = self._parser.module()
n = pr.Name(self._parser.module, [(name, (0, 0))], n = pr.Name(self._parser.module(), [(name, (0, 0))],
(0, 0), (0, 0), scope) (0, 0), (0, 0), scope)
completions.append((n, scope)) completions.append((n, scope))
return completions return completions

View File

@@ -1,8 +1,11 @@
import re import re
import os
import sys import sys
from jedi.cache import underscore_memoization from jedi import cache
from jedi.parser import tokenize from jedi.parser import tokenize
from jedi.parser import fast
from jedi.parser import representation
from jedi import debug from jedi import debug
@@ -21,7 +24,7 @@ class UserContext(object):
self._line_temp = None self._line_temp = None
self._relevant_temp = None self._relevant_temp = None
@underscore_memoization @cache.underscore_memoization
def get_path_until_cursor(self): def get_path_until_cursor(self):
""" Get the path under the cursor. """ """ Get the path under the cursor. """
path, self._start_cursor_pos = self._calc_path_until_cursor(self.position) path, self._start_cursor_pos = self._calc_path_until_cursor(self.position)
@@ -175,3 +178,39 @@ class UserContext(object):
def get_position_line(self): def get_position_line(self):
return self.get_line(self.position[0])[:self.position[1]] return self.get_line(self.position[0])[:self.position[1]]
class UserContextParser(object):
def __init__(self, source, path, position, user_context):
self._source = source
self._path = path and os.path.abspath(path)
self._position = position
self._user_context = user_context
@cache.underscore_memoization
def _parser(self):
cache.invalidate_star_import_cache(self._path)
parser = fast.FastParser(self._source, self._path, self._position)
# Don't pickle that module, because the main module is changing quickly
cache.save_parser(self._path, None, parser, pickling=False)
return parser
def user_stmt(self, is_completion=False):
user_stmt = self._parser().user_stmt
debug.speed('parsed')
if is_completion and not user_stmt:
# for statements like `from x import ` (cursor not in statement)
pos = next(self._user_context.get_context(yield_positions=True))
last_stmt = pos and self._parser().module.get_statement_for_position(
pos, include_imports=True)
if isinstance(last_stmt, representation.Import):
user_stmt = last_stmt
return user_stmt
def user_scope(self):
return self._parser().user_scope
def module(self):
return self._parser().module

View File

@@ -104,7 +104,7 @@ def extract(script, new_name):
new_lines = common.source_to_unicode(script.source).splitlines() new_lines = common.source_to_unicode(script.source).splitlines()
old_lines = new_lines[:] old_lines = new_lines[:]
user_stmt = script._parser.user_stmt user_stmt = script._parser.user_stmt()
# TODO care for multiline extracts # TODO care for multiline extracts
dct = {} dct = {}