Fix fast parser tests.

This commit is contained in:
Dave Halter
2015-01-24 00:06:16 +01:00
parent 8569651bf4
commit 4d6afd3c99
8 changed files with 52 additions and 36 deletions

View File

@@ -171,7 +171,7 @@ class Script(object):
debug.speed('completions start') debug.speed('completions start')
path = self._user_context.get_path_until_cursor() path = self._user_context.get_path_until_cursor()
# dots following an int are not the start of a completion but a float # Dots following an int are not the start of a completion but a float
# literal. # literal.
if re.search(r'^\d\.$', path): if re.search(r'^\d\.$', path):
return [] return []

View File

@@ -302,6 +302,7 @@ class Parser(object):
if typ == token.OP: if typ == token.OP:
typ = token.opmap[value] typ = token.opmap[value]
#print(start_pos, tokenize.tok_name[typ], repr(value)) #print(start_pos, tokenize.tok_name[typ], repr(value))
print(token.tok_name[typ], value, start_pos)
yield typ, value, prefix, start_pos yield typ, value, prefix, start_pos
def __repr__(self): def __repr__(self):

View File

@@ -12,7 +12,7 @@ from jedi.parser import Parser
from jedi.parser import tree as pr from jedi.parser import tree as pr
from jedi.parser import tokenize from jedi.parser import tokenize
from jedi import cache from jedi import cache
from jedi.parser.tokenize import (source_tokens, FLOWS, NEWLINE, COMMENT, from jedi.parser.tokenize import (source_tokens, FLOWS, NEWLINE,
ENDMARKER, INDENT, DEDENT) ENDMARKER, INDENT, DEDENT)
@@ -48,6 +48,13 @@ class FastModule(pr.Module, pr.Simple):
""" """
return MergedNamesDict([m.used_names for m in self.modules]) return MergedNamesDict([m.used_names for m in self.modules])
def _search_in_scope(self, typ):
return pr.Scope._search_in_scope(self, typ)
@property
def subscopes(self):
return self._search_in_scope(pr.Scope)
def __repr__(self): def __repr__(self):
return "<fast.%s: %s@%s-%s>" % (type(self).__name__, self.name, return "<fast.%s: %s@%s-%s>" % (type(self).__name__, self.name,
self.start_pos[0], self.end_pos[0]) self.start_pos[0], self.end_pos[0])
@@ -57,8 +64,10 @@ class MergedNamesDict(object):
def __init__(self, dicts): def __init__(self, dicts):
self.dicts = dicts self.dicts = dicts
def __iter__(self):
return iter(set(key for dct in self.dicts for key in dct))
def __getitem__(self, value): def __getitem__(self, value):
print(value, self.dicts)
return list(chain.from_iterable(dct.get(value, []) for dct in self.dicts)) return list(chain.from_iterable(dct.get(value, []) for dct in self.dicts))
def values(self): def values(self):
@@ -88,7 +97,7 @@ class ParserNode(object):
self._fast_module = fast_module self._fast_module = fast_module
self.parent = parent self.parent = parent
self.node_children = [] self._node_children = []
self.code = None self.code = None
self.hash = None self.hash = None
self.parser = None self.parser = None
@@ -124,20 +133,20 @@ class ParserNode(object):
self._is_generator = scope.is_generator self._is_generator = scope.is_generator
""" """
self.node_children = [] self._node_children = []
def reset_node(self): def reset_node(self):
""" """
Removes changes that were applied in this class. Removes changes that were applied in this class.
""" """
nd_scope = self._names_dict_scope self._node_children = []
self._content_scope.children = list(self._old_children) scope = self._content_scope
scope.children = list(self._old_children)
try: try:
# This works if it's a MergedNamesDict. # This works if it's a MergedNamesDict.
# We are correcting it, because the MergedNamesDicts are artificial # We are correcting it, because the MergedNamesDicts are artificial
# and can change after closing a node. # and can change after closing a node.
print('module.names_dict', nd_scope.names_dict) scope.names_dict = scope.names_dict.dicts[0]
nd_scope.names_dict = nd_scope.names_dict.dicts[0]
except AttributeError: except AttributeError:
pass pass
@@ -163,17 +172,17 @@ class ParserNode(object):
Closes the current parser node. This means that after this no further Closes the current parser node. This means that after this no further
nodes should be added anymore. nodes should be added anymore.
""" """
print('CLOSE NODE', self.parent, self.node_children) print('CLOSE NODE', self.parent, self._node_children)
if self.parser: print(self.parser.module.names_dict, [p.parser.module.names_dict for p in if self.parser: print(self.parser.module.names_dict, [p.parser.module.names_dict for p in
self.node_children]) self._node_children])
# We only need to replace the dict if multiple dictionaries are used: # We only need to replace the dict if multiple dictionaries are used:
if self.node_children: if self._node_children:
dcts = [n.parser.module.names_dict for n in self.node_children] dcts = [n.parser.module.names_dict for n in self._node_children]
if self.parser is not None: if self.parser is not None:
# The first Parser node contains all the others and is # The first Parser node contains all the others and is
# typically empty. # typically empty.
dcts.insert(0, self._names_dict_scope.names_dict) dcts.insert(0, self._names_dict_scope.names_dict)
print('DCTS', dcts) print('DCTS', self.parser, dcts, self._node_children)
self._content_scope.names_dict = MergedNamesDict(dcts) self._content_scope.names_dict = MergedNamesDict(dcts)
def parent_until_indent(self, indent=None): def parent_until_indent(self, indent=None):
@@ -200,7 +209,7 @@ class ParserNode(object):
try: try:
el = [r for r in module.returns if r is not None][0] el = [r for r in module.returns if r is not None][0]
except IndexError: except IndexError:
return self.parent.indent + 1 el = module.children[0]
return el.start_pos[1] return el.start_pos[1]
def _set_items(self, parser, set_parent=False): def _set_items(self, parser, set_parent=False):
@@ -216,15 +225,13 @@ class ParserNode(object):
def add_node(self, node, line_offset): def add_node(self, node, line_offset):
"""Adding a node means adding a node that was already added earlier""" """Adding a node means adding a node that was already added earlier"""
print('ADD')
# Changing the line offsets is very important, because if they don't # Changing the line offsets is very important, because if they don't
# fit, all the start_pos values will be wrong. # fit, all the start_pos values will be wrong.
m = node.parser.module m = node.parser.module
m.line_offset += line_offset + 1 - m.start_pos[0] m.line_offset += line_offset + 1 - m.start_pos[0]
self._fast_module.modules.append(m) self._fast_module.modules.append(m)
node.node_children = [] self._node_children.append(node)
self.node_children.append(node)
# Insert parser objects into current structure. We only need to set the # Insert parser objects into current structure. We only need to set the
# parents and children in a good way. # parents and children in a good way.
@@ -260,7 +267,7 @@ class ParserNode(object):
""" """
Returns all nodes including nested ones. Returns all nodes including nested ones.
""" """
for n in self.node_children: for n in self._node_children:
yield n yield n
for y in n.all_sub_nodes(): for y in n.all_sub_nodes():
yield y yield y
@@ -373,7 +380,6 @@ class FastParser(use_metaclass(CachedFastParser)):
# print '#'*45,line_offset, p and p.module.end_pos, '\n', code_part # print '#'*45,line_offset, p and p.module.end_pos, '\n', code_part
self.current_node = self._get_node(code_part, code[start:], self.current_node = self._get_node(code_part, code[start:],
line_offset, nodes, not is_first) line_offset, nodes, not is_first)
print('HmmmmA', self.current_node.parser.module.names_dict)
if False and is_first and self.current_node.parser.module.subscopes: if False and is_first and self.current_node.parser.module.subscopes:
print('NOXXXX') print('NOXXXX')
@@ -446,7 +452,7 @@ class FastParser(use_metaclass(CachedFastParser)):
node = ParserNode(self.module, self.current_node) node = ParserNode(self.module, self.current_node)
end = p.module.end_pos[0] end = p.module.end_pos[0]
print('\nACTUALLY PARSING\n', end, len(self._lines)) print('\nACTUALLY PARSING', end, len(self._lines))
if len(self._lines) != end: if len(self._lines) != end:
# The actual used code_part is different from the given code # The actual used code_part is different from the given code
# part, because of docstrings for example there's a chance that # part, because of docstrings for example there's a chance that
@@ -482,7 +488,6 @@ class FastTokenizer(object):
self._returned_endmarker = False self._returned_endmarker = False
def __iter__(self): def __iter__(self):
print('NEW')
return self return self
def next(self): def next(self):
@@ -497,6 +502,7 @@ class FastTokenizer(object):
if typ == ENDMARKER: if typ == ENDMARKER:
self._closed = True self._closed = True
self._returned_endmarker = True self._returned_endmarker = True
return current
self.previous = self.current self.previous = self.current
self.current = current self.current = current
@@ -511,19 +517,22 @@ class FastTokenizer(object):
self._indent_counter -= 1 self._indent_counter -= 1
return current return current
# Check for NEWLINE with a valid token here, which symbolizes the if self.previous[0] == DEDENT and not self._in_flow:
# indent. print('w', self.current, self.previous, self._first_stmt)
# Ignore comments/newlines, irrelevant for indentation. self._first_stmt = False
if self.previous[0] in (None, NEWLINE, DEDENT) \ return self._close()
and typ not in (COMMENT, NEWLINE): elif self.previous[0] in (None, NEWLINE, INDENT):
# print c, tok_name[c[0]] # Check for NEWLINE, which symbolizes the indent.
print('X', repr(value), tokenize.tok_name[typ])
indent = start_pos[1] indent = start_pos[1]
print(indent, self._parser_indent)
if self._parentheses_level: if self._parentheses_level:
# parentheses ignore the indentation rules. # parentheses ignore the indentation rules.
pass pass
elif indent < self._parser_indent: # -> dedent elif indent < self._parser_indent: # -> dedent
self._parser_indent = indent self._parser_indent = indent
self._new_indent = False self._new_indent = False
print(self._in_flow, indent, self._old_parser_indent)
if not self._in_flow or indent < self._old_parser_indent: if not self._in_flow or indent < self._old_parser_indent:
return self._close() return self._close()
@@ -562,7 +571,7 @@ class FastTokenizer(object):
if self._first_stmt: if self._first_stmt:
# Continue like nothing has happened, because we want to enter # Continue like nothing has happened, because we want to enter
# the first class/function. # the first class/function.
self._first_stmt = True self._first_stmt = False
print('NOOO', self.current) print('NOOO', self.current)
return self.current return self.current
else: else:
@@ -577,6 +586,7 @@ class FastTokenizer(object):
return tokenize.DEDENT, '', start_pos, '' return tokenize.DEDENT, '', start_pos, ''
elif not self._returned_endmarker: elif not self._returned_endmarker:
self._returned_endmarker = True self._returned_endmarker = True
return tokenize.ENDMARKER, '', start_pos, '' print('end')
return ENDMARKER, '', start_pos, ''
else: else:
raise StopIteration raise StopIteration

View File

@@ -78,7 +78,9 @@ with_stmt: 'with' with_item (',' with_item)* ':' suite
with_item: test ['as' expr] with_item: test ['as' expr]
# NB compile.c makes sure that the default except clause is last # NB compile.c makes sure that the default except clause is last
except_clause: 'except' [test ['as' NAME]] except_clause: 'except' [test ['as' NAME]]
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT # Edit by David Halter: The stmt is now optional. This reflects how Jedi allows
# classes and functions to be empty, which is beneficial for autocompletion.
suite: simple_stmt | NEWLINE INDENT stmt* DEDENT
test: or_test ['if' or_test 'else' test] | lambdef test: or_test ['if' or_test 'else' test] | lambdef
test_nocond: or_test | lambdef_nocond test_nocond: or_test | lambdef_nocond

View File

@@ -23,7 +23,7 @@ class ParseError(Exception):
def __init__(self, msg, type, value, start_pos): def __init__(self, msg, type, value, start_pos):
Exception.__init__(self, "%s: type=%r, value=%r, start_pos=%r" % Exception.__init__(self, "%s: type=%r, value=%r, start_pos=%r" %
(msg, type, value, start_pos)) (msg, tokenize.tok_name[type], value, start_pos))
self.msg = msg self.msg = msg
self.type = type self.type = type
self.value = value self.value = value

View File

@@ -254,7 +254,7 @@ def generate_tokens(readline, line_offset=0):
while True: while True:
indent = indents.pop() indent = indents.pop()
if indent > start: if indent > start:
yield DEDENT, '', (lnum, 0), '' yield DEDENT, '', spos, ''
else: else:
indents.append(indent) indents.append(indent)
break break
@@ -269,5 +269,5 @@ def generate_tokens(readline, line_offset=0):
yield OP, token, spos, prefix yield OP, token, spos, prefix
for indent in indents[1:]: for indent in indents[1:]:
yield DEDENT, '', (lnum, 0), '' yield DEDENT, '', spos, ''
yield ENDMARKER, '', (lnum, 0), prefix yield ENDMARKER, '', spos, prefix

View File

@@ -284,8 +284,11 @@ class UserContextParser(object):
user_stmt = self.user_stmt() user_stmt = self.user_stmt()
if user_stmt is None: if user_stmt is None:
def scan(scope): def scan(scope):
print(scope, scope.subscopes)
for s in scope.subscopes + list(reversed(scope.flows)): for s in scope.subscopes + list(reversed(scope.flows)):
if isinstance(s, (pr.Scope, pr.Flow)): if isinstance(s, (pr.Scope, pr.Flow)):
print(s, self._position, s.end_pos, s.children,
s.children[-2])
if s.start_pos <= self._position <= s.end_pos: if s.start_pos <= self._position <= s.end_pos:
if isinstance(s, pr.Flow): if isinstance(s, pr.Flow):
return s return s

View File

@@ -74,8 +74,8 @@ def test_change_and_undo():
fp(func_before + 'a') fp(func_before + 'a')
fp(func_before + 'b') fp(func_before + 'b')
fp(func_before + 'a') fp(func_before + 'a')
fp(func_before + 'a')
cache.parser_cache.pop(None, None)
fp('a') fp('a')
fp('b') fp('b')
fp('a') fp('a')