Some progress and bugfixes.

This commit is contained in:
Dave Halter
2016-08-26 12:47:02 +02:00
parent 1e5ad467d3
commit 42e5777620
3 changed files with 73 additions and 54 deletions

View File

@@ -317,7 +317,7 @@ class ParserWithRecovery(Parser):
) )
def parse(self, tokenizer): def parse(self, tokenizer):
root_node = super(ParserWithRecovery, self).parse(self._tokenize(self._tokenize(tokenizer))) root_node = super(ParserWithRecovery, self).parse(self._tokenize(tokenizer))
self.module = root_node self.module = root_node
self.module.used_names = self._used_names self.module.used_names = self._used_names
self.module.path = self._module_path self.module.path = self._module_path

View File

@@ -88,17 +88,17 @@ class DiffParser():
lines_old = splitlines(self._parser.source, keepends=True) lines_old = splitlines(self._parser.source, keepends=True)
sm = difflib.SequenceMatcher(None, lines_old, lines_new) sm = difflib.SequenceMatcher(None, lines_old, lines_new)
print(lines_old, lines_new) print(len(lines_old), len(lines_new), lines_old, lines_new)
for operation, i1, i2, j1, j2 in sm.get_opcodes(): for operation, i1, i2, j1, j2 in sm.get_opcodes():
print(operation) print(operation, i1, i2, j1, j2)
if operation == 'equal': if operation == 'equal':
line_offset = j1 - i1 line_offset = j1 - i1
self._copy_from_old_parser(line_offset, i2 + 1, j2 + 1) self._copy_from_old_parser(line_offset, i2 + 1, j2)
elif operation == 'replace': elif operation == 'replace':
self._delete_count += 1 self._delete_count += 1
self._insert(j2 + 1) self._insert(j2)
elif operation == 'insert': elif operation == 'insert':
self._insert(j2 + 1) self._insert(j2)
else: else:
assert operation == 'delete' assert operation == 'delete'
self._delete_count += 1 # For statistics self._delete_count += 1 # For statistics
@@ -107,6 +107,7 @@ class DiffParser():
self._module.used_names = self._temp_module.used_names self._module.used_names = self._temp_module.used_names
self._module.children = self._new_children self._module.children = self._new_children
# TODO insert endmarker # TODO insert endmarker
print(self._module.get_code())
def _insert(self, until_line_new): def _insert(self, until_line_new):
self._insert_count += 1 self._insert_count += 1
@@ -135,6 +136,7 @@ class DiffParser():
nodes.append(node) nodes.append(node)
if nodes: if nodes:
print('COPY', until_line_new)
parent = self._insert_nodes(nodes) parent = self._insert_nodes(nodes)
self._update_names_dict(parent, nodes) self._update_names_dict(parent, nodes)
# TODO remove dedent at end # TODO remove dedent at end
@@ -164,18 +166,33 @@ class DiffParser():
return node return node
def _insert_nodes(self, nodes): def _insert_nodes(self, nodes):
"""
Returns the scope that a node is a part of.
"""
# Needs to be done before resetting the parsed # Needs to be done before resetting the parsed
before_node = self._get_before_insertion_node() before_node = self._get_before_insertion_node()
last_leaf = nodes[-1].last_leaf() last_leaf = nodes[-1].last_leaf()
if last_leaf.value == '\n': is_endmarker = last_leaf.type == self.endmarker_type
last_non_endmarker = last_leaf
if is_endmarker:
try:
last_non_endmarker = last_leaf.get_previous_leaf()
except IndexError:
# If the parsed part is empty, nevermind and continue with the
# endmarker.
pass
print(last_non_endmarker)
if last_non_endmarker.type in ('newline', 'dedent'):
# Newlines end on the next line, which means that they would cover # Newlines end on the next line, which means that they would cover
# the next line. That line is not fully parsed at this point. # the next line. That line is not fully parsed at this point.
self._parsed_until_line = last_leaf.end_pos[0] - 1 self._parsed_until_line = last_leaf.end_pos[0] - 1
else: else:
self._parsed_until_line = last_leaf.end_pos[0] self._parsed_until_line = last_leaf.end_pos[0]
print('parsed_until', last_leaf.end_pos, self._parsed_until_line)
if last_leaf.type == self.endmarker_type: if is_endmarker:
first_leaf = nodes[0].first_leaf() first_leaf = nodes[0].first_leaf()
first_leaf.prefix = self._prefix + first_leaf.prefix first_leaf.prefix = self._prefix + first_leaf.prefix
self._prefix = last_leaf.prefix self._prefix = last_leaf.prefix
@@ -183,16 +200,22 @@ class DiffParser():
nodes = nodes[:-1] nodes = nodes[:-1]
if not nodes: if not nodes:
return self._module return self._module
print("X", nodes)
# 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
parent = self._temp_module parent = self._module
else: else:
assert nodes[0].type != 'newline'
line_indentation = nodes[0].start_pos[1] line_indentation = nodes[0].start_pos[1]
while True: while True:
p_children = before_node.parent.children p_children = before_node.parent.children
indentation = p_children[0].start_pos[1] if before_node.parent.type == 'suite':
# A suite starts with NEWLINE, INDENT, ...
indentation = p_children[2].start_pos[1]
else:
indentation = p_children[0].start_pos[1]
if line_indentation < indentation: # Dedent if line_indentation < indentation: # Dedent
# We might be at the most outer layer: modules. We # We might be at the most outer layer: modules. We
@@ -209,6 +232,8 @@ class DiffParser():
# TODO check if the indentation is lower than the last statement # TODO check if the indentation is lower than the last statement
# and add a dedent error leaf. # and add a dedent error leaf.
# TODO do the same for indent error leafs. # TODO do the same for indent error leafs.
print('before_node', before_node)
print(nodes)
p_children += nodes p_children += nodes
parent = before_node.parent parent = before_node.parent
break break
@@ -216,14 +241,32 @@ class DiffParser():
# Reset the parents # Reset the parents
for node in nodes: for node in nodes:
node.parent = parent node.parent = parent
if parent.type == 'suite':
return parent.parent
return parent return parent
def _update_names_dict(self, parent_node, nodes): def _get_before_insertion_node(self):
assert parent_node.type in ('suite', 'file_input') if not self._new_children:
if parent_node.type == 'suite': return None
parent_node = parent_node.parent
names_dict = parent_node.names_dict line = self._parsed_until_line + 1
leaf = self._temp_module.last_leaf()
node = leaf
while True:
parent = node.parent
print('get_ins', parent)
if parent.type in ('suite', 'file_input'):
print('get_ins', node)
print('get_ins', line, node.end_pos)
assert node.end_pos[0] <= line
assert node.end_pos[1] == 0
return node
node = parent
def _update_names_dict(self, scope_node, nodes):
assert scope_node.type in ('suite', 'file_input')
names_dict = scope_node.names_dict
def scan(nodes): def scan(nodes):
for node in nodes: for node in nodes:
@@ -238,8 +281,8 @@ class DiffParser():
scan(nodes) scan(nodes)
def _merge_parsed_node(self, parent_node, parsed_node): def _merge_parsed_node(self, scope_node, parsed_node):
_merge_names_dicts(parent_node.names_dict, parsed_node.names_dict) _merge_names_dicts(scope_node.names_dict, parsed_node.names_dict)
_merge_names_dicts(self._temp_module.used_names, parsed_node.used_names) _merge_names_dicts(self._temp_module.used_names, parsed_node.used_names)
def _divide_node(self, node, until_line): def _divide_node(self, node, until_line):
@@ -268,35 +311,6 @@ class DiffParser():
break break
return new_node return new_node
def _get_before_insertion_node(self):
if not self._new_children:
return None
line = self._parsed_until_line + 1
leaf = self._module.last_leaf()
'''
print(line)
leaf = self._module.get_leaf_for_position((line, 0), include_prefixes=False)
while leaf.type != 'newline':
try:
leaf = leaf.get_previous_leaf()
except IndexError:
# TODO
raise NotImplementedError
'''
node = leaf
while True:
parent = node.parent
print(parent)
if parent.type in ('suite', 'file_input'):
print(node)
print(line, node.end_pos)
assert node.end_pos[0] <= line
assert node.end_pos[1] == 0
return node
node = parent
def _get_old_line_stmt(self, old_line): def _get_old_line_stmt(self, old_line):
leaf = self._module.get_leaf_for_position((old_line, 0), include_prefixes=True) leaf = self._module.get_leaf_for_position((old_line, 0), include_prefixes=True)
if leaf.get_start_pos_of_prefix()[0] == old_line: if leaf.get_start_pos_of_prefix()[0] == old_line:
@@ -314,6 +328,9 @@ class DiffParser():
nodes = self._get_children_nodes(node) nodes = self._get_children_nodes(node)
parent = self._insert_nodes(nodes) parent = self._insert_nodes(nodes)
self._merge_parsed_node(parent, node) self._merge_parsed_node(parent, node)
#if until_line - 1 == len(self._lines_new):
# We are done in any case. This is special case, because the l
#return
def _get_children_nodes(self, node): def _get_children_nodes(self, node):
nodes = node.children nodes = node.children
@@ -327,9 +344,11 @@ class DiffParser():
return nodes return nodes
def _parse_scope_node(self, until_line): def _parse_scope_node(self, until_line):
print('PARSE', until_line, self._parsed_until_line)
# 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._lines_new[self._parsed_until_line + 1:] lines_after = self._lines_new[self._parsed_until_line:]
print('x', self._parsed_until_line, lines_after, until_line)
tokenizer = self._diff_tokenize( tokenizer = self._diff_tokenize(
lines_after, lines_after,
until_line, until_line,
@@ -362,7 +381,7 @@ class DiffParser():
tokens = generate_tokens(lambda: next(l, '')) tokens = generate_tokens(lambda: next(l, ''))
for typ, string, start_pos, prefix in tokens: for typ, string, start_pos, prefix in tokens:
start_pos = start_pos[0] + line_offset, start_pos[1] start_pos = start_pos[0] + line_offset, start_pos[1]
if typ == 'indent': if typ == tokenize.INDENT:
indent_count += 1 indent_count += 1
if is_first_token: if is_first_token:
omited_first_indent = True omited_first_indent = True
@@ -370,13 +389,13 @@ class DiffParser():
# we only parse part of the file. These indents would only # we only parse part of the file. These indents would only
# get parsed as error leafs, which doesn't make any sense. # get parsed as error leafs, which doesn't make any sense.
continue continue
elif typ == 'dedent': elif typ == tokenize.DEDENT:
indent_count -= 1 indent_count -= 1
if omited_first_indent and indent_count == 0: if omited_first_indent and indent_count == 0:
# We are done here, only thing that can come now is an # We are done here, only thing that can come now is an
# endmarker or another dedented code block. # endmarker or another dedented code block.
break break
elif typ == 'newline' and start_pos[0] >= until_line: elif typ == tokenize.NEWLINE and start_pos[0] >= until_line:
yield tokenize.TokenInfo(typ, string, start_pos, prefix) yield tokenize.TokenInfo(typ, string, start_pos, prefix)
# Check if the parser is actually in a valid suite state. # Check if the parser is actually in a valid suite state.
if 1: if 1:
@@ -388,6 +407,7 @@ class DiffParser():
is_first_token = False is_first_token = False
print('tok', typ, string, start_pos)
yield tokenize.TokenInfo(typ, string, start_pos, prefix) yield tokenize.TokenInfo(typ, string, start_pos, prefix)
typ, string, start_pos, prefix = next(tokens) typ, string, start_pos, prefix = next(tokens)

View File

@@ -16,7 +16,7 @@ def test_add_to_end():
help of caches, this is an example that didn't work. help of caches, this is an example that didn't work.
""" """
a = dedent(""" a = dedent("""\
class Abc(): class Abc():
def abc(self): def abc(self):
self.x = 3 self.x = 3
@@ -24,16 +24,15 @@ def test_add_to_end():
class Two(Abc): class Two(Abc):
def g(self): def g(self):
self self
""") # ^ here is the first completion """) # ^ here is the first completion
b = " def h(self):\n" \ b = " def h(self):\n" \
" self." " self."
assert jedi.Script(a, 8, 12, 'example.py').completions() assert jedi.Script(a, 7, 12, 'example.py').completions()
assert jedi.Script(a + b, path='example.py').completions() assert jedi.Script(a + b, path='example.py').completions()
a = a[:-1] + '.\n' a = a[:-1] + '.\n'
assert jedi.Script(a, 8, 13, 'example.py').completions() assert jedi.Script(a, 7, 13, 'example.py').completions()
assert jedi.Script(a + b, path='example.py').completions() assert jedi.Script(a + b, path='example.py').completions()