1
0
forked from VimPlug/jedi

Some asserts pass now in the tests.

This commit is contained in:
Dave Halter
2017-01-19 18:26:50 +01:00
parent 3bf5f93edd
commit ef31c3d1f4
+105 -188
View File
@@ -114,8 +114,6 @@ def _update_positions(nodes, line_offset):
class DiffParser(object): class DiffParser(object):
endmarker_type = 'endmarker'
def __init__(self, parser): def __init__(self, parser):
self._parser = parser self._parser = parser
self._grammar = self._parser._grammar self._grammar = self._parser._grammar
@@ -130,7 +128,6 @@ class DiffParser(object):
self._old_children = self._old_module.children self._old_children = self._old_module.children
self._new_used_names = {} self._new_used_names = {}
self._prefix = ''
self._last_prefix = '' self._last_prefix = ''
self._nodes_stack = _NodesStack(self._old_module) self._nodes_stack = _NodesStack(self._old_module)
@@ -193,7 +190,7 @@ class DiffParser(object):
self._cleanup() self._cleanup()
if self._added_newline: if self._added_newline:
self._parser.remove_last_newline() self._parser.remove_last_newline()
self._parsed_until_line -= 1 self._nodes_stack.parsed_until_line -= 1
self._parser.source = ''.join(lines_new) self._parser.source = ''.join(lines_new)
@@ -205,13 +202,13 @@ class DiffParser(object):
copied_nodes = [None] copied_nodes = [None]
while until_line_new > self._parsed_until_line: while until_line_new > self._parsed_until_line:
parsed_until_line_old = self._parsed_until_line - line_offset parsed_until_line_old = self._nodes_stack.parsed_until_line - line_offset
line_stmt = self._get_old_line_stmt(parsed_until_line_old + 1) line_stmt = self._get_old_line_stmt(parsed_until_line_old + 1)
if line_stmt is None: if line_stmt is None:
# Parse 1 line at least. We don't need more, because we just # Parse 1 line at least. We don't need more, because we just
# want to get into a state where the old parser has statements # want to get into a state where the old parser has statements
# again that can be copied (e.g. not lines within parentheses). # again that can be copied (e.g. not lines within parentheses).
self._parse(self._parsed_until_line + 1) self._parse(self._nodes_stack.parsed_until_line + 1)
elif not copied_nodes: elif not copied_nodes:
# We have copied as much as possible (but definitely not too # We have copied as much as possible (but definitely not too
# much). Therefore we just parse the rest. # much). Therefore we just parse the rest.
@@ -219,18 +216,16 @@ class DiffParser(object):
# that is not finished. # that is not finished.
self._parse(until_line_new) self._parse(until_line_new)
else: else:
p_children = line_stmt.parent.children
index = p_children.index(line_stmt)
copied_nodes = self._nodes_stack.copy_nodes( copied_nodes = self._nodes_stack.copy_nodes(
line_stmt.parent.children, p_children[index:],
until_line_old, until_line_old,
line_offset line_offset
) )
#p_children = line_stmt.parent.children
#index = p_children.index(line_stmt)
# Match all the nodes that are in the wanted range. # Match all the nodes that are in the wanted range.
#nodes = self._divide_nodes(p_children[index:], until_line_old)
if copied_nodes: if copied_nodes:
#self._insert_nodes(nodes)
self._copy_count += 1 self._copy_count += 1
from_ = copied_nodes[0].get_start_pos_of_prefix()[0] from_ = copied_nodes[0].get_start_pos_of_prefix()[0]
@@ -259,6 +254,8 @@ class DiffParser(object):
""" """
Returns the scope that a node is a part of. Returns the scope that a node is a part of.
""" """
assert False
last_leaf = nodes[-1].last_leaf() last_leaf = nodes[-1].last_leaf()
is_endmarker = last_leaf.type == self.endmarker_type is_endmarker = last_leaf.type == self.endmarker_type
self._last_prefix = '' self._last_prefix = ''
@@ -298,9 +295,10 @@ class DiffParser(object):
if not nodes: if not nodes:
return return
self._nodes_stack.add_nodes(nodes, self._parsed_until_line + 1) self._nodes_stack.add_nodes(nodes)
return return
'''
# Now the preparations are done. We are inserting the nodes. # Now the preparations are done. We are inserting the nodes.
if before_node is None: # Everything is empty. if before_node is None: # Everything is empty.
self._new_children += nodes self._new_children += nodes
@@ -339,12 +337,13 @@ class DiffParser(object):
# return new_parent.get_parent_scope() # return new_parent.get_parent_scope()
#return new_parent #return new_parent
'''
def _get_before_insertion_node(self): def _get_before_insertion_node(self):
if self._nodes_stack.is_empty(): if self._nodes_stack.is_empty():
return None return None
line = self._parsed_until_line + 1 line = self._nodes_stack.parsed_until_line + 1
node = self._new_module.last_leaf() node = self._new_module.last_leaf()
while True: while True:
parent = node.parent parent = node.parent
@@ -354,136 +353,16 @@ class DiffParser(object):
return node return node
node = parent node = parent
def _divide_node(self, node, until_line):
if node.type not in ('classdef', 'funcdef'):
return None
suite = node.children[-1]
if suite.type != 'suite':
return None
nodes = self._divide_nodes(suite.children, until_line)
if len(nodes) < 2:
# A suite only with newline is not valid.
return None
new_node = copy.copy(node)
new_suite = copy.copy(suite)
# And now set the correct parents
for child in nodes:
child.parent = new_suite
new_suite.children = nodes
new_node.children = list(new_node.children)
new_node.children[-1] = new_suite
for child in new_node.children:
child.parent = new_node
return new_node
def _copy_divided_nodes(self, nodes):
parent = nodes[-1].last_leaf().get_parent_scope()
if parent == nodes[0].get_parent_scope():
check_nodes = nodes
else:
n = parent
while n is not None:
if isinstance(n, Flow):
parent = n.get_parent_scope()
n = n.parent
check_nodes = parent.children
last_node = check_nodes[-1]
if last_node.type == 'suite':
parent = last_node
check_nodes = parent.children
last_node = check_nodes[-1]
drop_node_count = 0
if last_node.type in ('error_leaf', 'error_node') or _is_flow_node(last_node):
# Error leafs/nodes don't have a defined start/end. Error
# nodes might not end with a newline (e.g. if there's an
# open `(`). Therefore ignore all of them unless they are
# succeeded with valid parser state.
# If we copy flows at the end, they might be continued
# after the copy limit (in the new parser).
n = last_node
# In this while loop we try to remove until we find a newline.
while True:
drop_node_count += 1
try:
n = check_nodes[-drop_node_count - 1]
except IndexError:
break
if n.last_leaf().type == 'newline':
break
if drop_node_count:
node = self._drop_last_node(nodes[-1], last_node, drop_node_count)
if node is None:
nodes = nodes[:-drop_node_count]
else:
nodes[-1] = node
return nodes
def _drop_last_node(self, base_node, last_node_to_drop, drop_node_count):
if base_node == last_node_to_drop:
return None
last_node = base_node.children[-1]
child = self._drop_last_node(last_node, last_node_to_drop, drop_node_count)
base_node = copy.copy(base_node)
if child is None:
if base_node.type == 'suite' and len(base_node.children) <= 1 + drop_node_count:
return None
if base_node.type in ('classdef', 'funcdef'):
return None
base_node.children = base_node.children[:-drop_node_count]
else:
base_node.children = list(base_node.children)
base_node.children[-1] = child
child.parent = base_node
for c in base_node.children:
c.parent = base_node
return base_node
def _divide_nodes(self, nodes, until_line):
"""
Breaks up scopes and returns only the part until the given line.
Tries to get the parts it can safely get and ignores the rest.
"""
new_nodes = []
for i, child in enumerate(nodes):
# TODO this check might take a bit of time for large files. We
# might want to change this to do more intelligent guessing or
# binary search.
if _get_last_line(child) > until_line:
node = self._divide_node(child, until_line)
if node is not None:
new_nodes.append(node)
break
else:
new_nodes.append(child)
if new_nodes:
return self._copy_divided_nodes(new_nodes)
return new_nodes
def _parse(self, until_line): def _parse(self, until_line):
""" """
Parses at least until the given line, but might just parse more until a Parses at least until the given line, but might just parse more until a
valid state is reached. valid state is reached.
""" """
while until_line > self._parsed_until_line: while until_line > self._nodes_stack.parsed_until_line:
node = self._try_parse_part(until_line) node = self._try_parse_part(until_line)
nodes = self._get_children_nodes(node) nodes = self._get_children_nodes(node)
self._insert_nodes(nodes) #self._insert_nodes(nodes)
self._nodes_stack.add_nodes(nodes)
_merge_used_names( _merge_used_names(
self._new_used_names, self._new_used_names,
node.used_names node.used_names
@@ -496,9 +375,6 @@ class DiffParser(object):
if first_element.type == 'error_leaf' and \ if first_element.type == 'error_leaf' and \
first_element.original_type == 'indent': first_element.original_type == 'indent':
assert False, str(nodes) assert False, str(nodes)
#assert nodes[-1].type == 'dedent'
## This means that the start and end leaf
#nodes = nodes[1:-1] + [nodes[-1]]
return nodes return nodes
@@ -511,12 +387,13 @@ class DiffParser(object):
self._parser_count += 1 self._parser_count += 1
# TODO speed up, shouldn't copy the whole list all the time. # TODO speed up, shouldn't copy the whole list all the time.
# memoryview? # memoryview?
lines_after = self._parser_lines_new[self._parsed_until_line:] parsed_until_line = self._nodes_stack.parsed_until_line
#print('parse_content', self._parsed_until_line, lines_after, until_line) lines_after = self._parser_lines_new[parsed_until_line:]
#print('parse_content', parsed_until_line, lines_after, until_line)
tokenizer = self._diff_tokenize( tokenizer = self._diff_tokenize(
lines_after, lines_after,
until_line, until_line,
line_offset=self._parsed_until_line line_offset=parsed_until_line
) )
self._active_parser = ParserWithRecovery( self._active_parser = ParserWithRecovery(
self._grammar, self._grammar,
@@ -538,24 +415,6 @@ class DiffParser(object):
if name.start_pos[0] in copied_line_numbers: if name.start_pos[0] in copied_line_numbers:
new_used_names.setdefault(key, []).append(name) new_used_names.setdefault(key, []).append(name)
# Add an endmarker.
try:
last_leaf = self._old_module.last_leaf()
end_pos = list(last_leaf.end_pos)
except IndexError:
end_pos = [1, 0]
lines = splitlines(self._prefix)
assert len(lines) > 0
if len(lines) == 1:
end_pos[1] += len(lines[0])
else:
end_pos[0] += len(lines) - 1
end_pos[1] = len(lines[-1])
endmarker = EndMarker('', tuple(end_pos), self._prefix + self._last_prefix)
endmarker.parent = self._old_module
self._old_module.children.append(endmarker)
def _diff_tokenize(self, lines, until_line, line_offset=0): def _diff_tokenize(self, lines, until_line, line_offset=0):
is_first_token = True is_first_token = True
omitted_first_indent = False omitted_first_indent = False
@@ -608,12 +467,12 @@ class DiffParser(object):
class _NodesStackNode(object): class _NodesStackNode(object):
def __init__(self, tree_node, parent): def __init__(self, tree_node, parent):
self.tree_node = tree_node self.tree_node = tree_node
self.children_groups = [] self._children_groups = []
self.parent = parent self.parent = parent
def close(self): def close(self):
children = [] children = []
for children_part, line_offset in self.children_groups: for children_part, line_offset in self._children_groups:
_update_positions(children_part, line_offset) _update_positions(children_part, line_offset)
children += children_part children += children_part
self.tree_node.children = children self.tree_node.children = children
@@ -622,26 +481,33 @@ class _NodesStackNode(object):
node.parent = self.tree_node node.parent = self.tree_node
def add(self, children, line_offset=0): def add(self, children, line_offset=0):
self.children_groups.append((children, line_offset)) self._children_groups.append((children, line_offset))
def update_last_children_group(self, new_children):
self._children_groups[-1] = (new_children, self._children_groups[-1][1])
class _NodesStack(object): class _NodesStack(object):
endmarker_type = 'endmarker'
def __init__(self, module): def __init__(self, module):
# Top of stack # Top of stack
self._tos = self._base_node = _NodesStackNode(module, None) self._tos = self._base_node = _NodesStackNode(module, None)
self._module = module
self.prefix = ''
self.parsed_until_line = 0
def is_empty(self): def is_empty(self):
return not self._base_node.children return not self._base_node.children
def _get_insertion_node(self, indentation_node, insertion_line): def _get_insertion_node(self, indentation_node):
indentation = indentation_node.start_pos[1] indentation = indentation_node.start_pos[1]
# find insertion node # find insertion node
node = self._tos node = self._tos
while True: while True:
tree_node = node.tree_node tree_node = node.tree_node
print(tree_node.type, node)
if tree_node.type == 'suite': if tree_node.type == 'suite':
# A suite starts with NEWLINE, ... # A suite starts with NEWLINE, ...
node_indentation = tree_node.children[1].start_pos[1] node_indentation = tree_node.children[1].start_pos[1]
@@ -658,29 +524,77 @@ class _NodesStack(object):
node.close() node.close()
node = node.parent node = node.parent
def add_nodes(self, tree_nodes, insertion_line): def add_nodes(self, tree_nodes):
"""insertion_line is simply here for debugging.""" tree_nodes = self._cleanup_nodes(tree_nodes)
if not tree_nodes: if not tree_nodes:
return return
assert tree_nodes[0].type != 'newline' assert tree_nodes[0].type != 'newline'
last_node = self._tos.tree_node last_node = self._tos.tree_node
assert last_node.end_pos[0] <= insertion_line assert last_node.end_pos[0] <= self.parsed_until_line
node = self._get_insertion_node(tree_nodes[0], insertion_line) node = self._get_insertion_node(tree_nodes[0])
assert node.tree_node.type in ('suite', 'file_input') assert node.tree_node.type in ('suite', 'file_input')
node.add(tree_nodes) node.add(tree_nodes)
self._update_tos(tree_nodes[-1]) self._update_tos(tree_nodes[-1])
def _cleanup_nodes(self, tree_nodes):
"""
Helps cleaning up the tree nodes that get inserted:
1. Sets parsed_until_line
2. Removes the endmarker
"""
last_leaf = tree_nodes[-1].last_leaf()
is_endmarker = last_leaf.type == self.endmarker_type
self._last_prefix = ''
if is_endmarker:
self.parsed_until_line = last_leaf.start_pos[0]
try:
separation = last_leaf.prefix.rindex('\n')
except ValueError:
pass
else:
# Remove the whitespace part of the prefix after a newline.
# That is not relevant if parentheses were opened. Always parse
# until the end of a line.
last_leaf.prefix, self._last_prefix = \
last_leaf.prefix[:separation + 1], last_leaf.prefix[separation + 1:]
if _last_leaf_is_newline(last_leaf):
self.parsed_until_line -= 1
else:
if last_leaf.type == 'newline':
# Newlines end on the next line, which means that they would cover
# the next line. That line is not fully parsed at this point.
self.parsed_until_line = last_leaf.start_pos[0]
else:
self.parsed_until_line = last_leaf.end_pos[0]
debug.dbg('set parsed_until %s', self.parsed_until_line)
first_leaf = tree_nodes[0].first_leaf()
#before_node = self._get_before_insertion_node()
first_leaf.prefix = self.prefix + first_leaf.prefix
self.prefix = ''
if is_endmarker:
self.prefix = last_leaf.prefix
tree_nodes = tree_nodes[:-1]
return tree_nodes
def copy_nodes(self, tree_nodes, until_line, line_offset): def copy_nodes(self, tree_nodes, until_line, line_offset):
""" """
Copies nodes from the old parser tree. Copies tree nodes from the old parser tree.
Returns the number of nodes that were copied. Returns the number of tree nodes that were copied.
""" """
tos = self._get_insertion_node(tree_nodes[0], until_line) tos = self._get_insertion_node(tree_nodes[0])
new_nodes, self._tos = self._xyz(tos, tos.tree_node.children, until_line, line_offset) new_nodes, self._tos = self._xyz(tos, tos.tree_node.children, until_line, line_offset)
if new_nodes:
self._tos.update_last_children_group(self._cleanup_nodes(new_nodes))
return new_nodes return new_nodes
def _xyz(self, tos, tree_nodes, until_line, line_offset): def _xyz(self, tos, tree_nodes, until_line, line_offset):
@@ -765,21 +679,6 @@ class _NodesStack(object):
if new_nodes: if new_nodes:
return self._copy_divided_nodes(new_nodes) return self._copy_divided_nodes(new_nodes)
return new_nodes return new_nodes
def _divide_node(self, node, until_line):
new_node = copy.copy(node)
new_suite = copy.copy(suite)
# And now set the correct parents
for child in nodes:
child.parent = new_suite
new_suite.children = nodes
new_node.children = list(new_node.children)
new_node.children[-1] = new_suite
for child in new_node.children:
child.parent = new_node
return new_node
def _copy_divided_nodes(self, nodes): def _copy_divided_nodes(self, nodes):
parent = nodes[-1].last_leaf().get_parent_scope() parent = nodes[-1].last_leaf().get_parent_scope()
if parent == nodes[0].get_parent_scope(): if parent == nodes[0].get_parent_scope():
@@ -847,3 +746,21 @@ class _NodesStack(object):
while node is not None: while node is not None:
node.close() node.close()
node = node.parent node = node.parent
# Add an endmarker.
try:
last_leaf = self._module.last_leaf()
end_pos = list(last_leaf.end_pos)
except IndexError:
end_pos = [1, 0]
lines = splitlines(self.prefix)
assert len(lines) > 0
if len(lines) == 1:
end_pos[1] += len(lines[0])
else:
end_pos[0] += len(lines) - 1
end_pos[1] = len(lines[-1])
endmarker = EndMarker('', tuple(end_pos), self.prefix + self._last_prefix)
endmarker.parent = self._module
self._module.children.append(endmarker)