mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-06 22:14:27 +08:00
Start implementing the node stack.
This commit is contained in:
@@ -8,6 +8,7 @@ fragments.
|
||||
import copy
|
||||
import re
|
||||
import difflib
|
||||
from collections import namedtuple
|
||||
|
||||
from jedi._compatibility import use_metaclass
|
||||
from jedi import settings
|
||||
@@ -93,7 +94,10 @@ def _last_leaf_is_newline(last_leaf):
|
||||
return True
|
||||
if last_leaf.prefix:
|
||||
return False
|
||||
try:
|
||||
previous_leaf = last_leaf.get_previous_leaf()
|
||||
except IndexError:
|
||||
return False
|
||||
return (previous_leaf.type == 'newline' or
|
||||
previous_leaf.type == 'error_leaf' and
|
||||
previous_leaf.original_type == 'newline')
|
||||
@@ -133,6 +137,8 @@ class DiffParser(object):
|
||||
self._prefix = ''
|
||||
self._last_prefix = ''
|
||||
|
||||
self._nodes_stack = _NodesStack(self._old_module)
|
||||
|
||||
def update(self, lines_new):
|
||||
'''
|
||||
The algorithm works as follows:
|
||||
@@ -184,7 +190,10 @@ class DiffParser(object):
|
||||
else:
|
||||
assert operation == 'delete'
|
||||
|
||||
# Cleanup (setting endmarker, used_names)
|
||||
# With this action all change will finally be applied and we have a
|
||||
# changed module.
|
||||
self._nodes_stack.close()
|
||||
|
||||
self._cleanup()
|
||||
if self._added_newline:
|
||||
self._parser.module = self._parser._parsed = self._new_module
|
||||
@@ -216,6 +225,7 @@ class DiffParser(object):
|
||||
if nodes:
|
||||
self._copy_count += 1
|
||||
_update_positions(nodes, line_offset)
|
||||
|
||||
self._insert_nodes(nodes)
|
||||
|
||||
from_ = nodes[0].get_start_pos_of_prefix()[0]
|
||||
@@ -249,9 +259,6 @@ class DiffParser(object):
|
||||
"""
|
||||
Returns the scope that a node is a part of.
|
||||
"""
|
||||
# Needs to be done before resetting the parsed
|
||||
before_node = self._get_before_insertion_node()
|
||||
|
||||
last_leaf = nodes[-1].last_leaf()
|
||||
is_endmarker = last_leaf.type == self.endmarker_type
|
||||
self._last_prefix = ''
|
||||
@@ -280,6 +287,7 @@ class DiffParser(object):
|
||||
debug.dbg('set parsed_until %s', self._parsed_until_line)
|
||||
|
||||
first_leaf = nodes[0].first_leaf()
|
||||
before_node = self._get_before_insertion_node()
|
||||
first_leaf.prefix = self._prefix + first_leaf.prefix
|
||||
self._prefix = ''
|
||||
|
||||
@@ -288,7 +296,9 @@ class DiffParser(object):
|
||||
|
||||
nodes = nodes[:-1]
|
||||
if not nodes:
|
||||
return self._new_module
|
||||
return
|
||||
|
||||
#self._nodes_stack.add_nodes(nodes, self._parsed_until_line + 1)
|
||||
|
||||
# Now the preparations are done. We are inserting the nodes.
|
||||
if before_node is None: # Everything is empty.
|
||||
@@ -324,10 +334,10 @@ class DiffParser(object):
|
||||
# Reset the parents
|
||||
for node in nodes:
|
||||
node.parent = new_parent
|
||||
if new_parent.type == 'suite':
|
||||
return new_parent.get_parent_scope()
|
||||
#if new_parent.type == 'suite':
|
||||
# return new_parent.get_parent_scope()
|
||||
|
||||
return new_parent
|
||||
#return new_parent
|
||||
|
||||
def _get_before_insertion_node(self):
|
||||
if not self._new_children:
|
||||
@@ -592,3 +602,71 @@ class DiffParser(object):
|
||||
continue
|
||||
|
||||
yield TokenInfo(typ, string, start_pos, prefix)
|
||||
|
||||
|
||||
class _NodesStackNode(namedtuple('_NodesStackNode', 'tree_node children parent')):
|
||||
def close(self):
|
||||
self.tree_node.children = self.children
|
||||
# Reset the parents
|
||||
for node in self.children:
|
||||
node.parent = self.tree_node
|
||||
|
||||
|
||||
class _NodesStack(object):
|
||||
def __init__(self, module):
|
||||
# Top of stack
|
||||
self._tos = _NodesStackNode(module, [], None)
|
||||
|
||||
def _get_insertion_node(self, indentation, insertion_line):
|
||||
# find insertion node
|
||||
node = self._tos
|
||||
while True:
|
||||
tree_node = node.tree_node
|
||||
if tree_node.type == 'suite':
|
||||
# A suite starts with NEWLINE, ...
|
||||
node_indentation = tree_node.children[1].start_pos[1]
|
||||
|
||||
if indentation >= node_indentation: # Not a Dedent
|
||||
# We might be at the most outer layer: modules. We
|
||||
# don't want to depend on the first statement
|
||||
# having the right indentation.
|
||||
return node
|
||||
|
||||
break
|
||||
elif tree_node.type == 'file_input':
|
||||
return node
|
||||
|
||||
node.close()
|
||||
node = node.parent
|
||||
|
||||
def add_nodes(self, nodes, insertion_line):
|
||||
"""insertion_line is simply here for debugging."""
|
||||
if not nodes:
|
||||
return
|
||||
|
||||
assert nodes[0].type != 'newline'
|
||||
last_node = self._tos.tree_node
|
||||
assert last_node.end_pos[0] <= insertion_line
|
||||
|
||||
indentation = nodes[0].start_pos[1]
|
||||
node = self._get_insertion_node(indentation, insertion_line)
|
||||
assert node.tree_node.type in ('suite', 'file_input')
|
||||
node.children += nodes
|
||||
self._update_tos(nodes[-1])
|
||||
|
||||
def _update_tos(self, tree_node):
|
||||
if tree_node.type in ('suite', 'file_input'):
|
||||
self._tos = _NodesStackNode(tree_node, list(tree_node.children), self._tos)
|
||||
|
||||
try:
|
||||
last_child = tree_node.children[-1]
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
self._update_tos(last_child)
|
||||
|
||||
def close(self):
|
||||
node = self._tos
|
||||
while node is not None:
|
||||
node.close()
|
||||
node = node.parent
|
||||
|
||||
@@ -384,3 +384,28 @@ def test_totally_wrong_whitespace(differ):
|
||||
|
||||
differ.initialize(code1)
|
||||
differ.parse(code2, parsers=3, copies=1, expect_error_leaves=True)
|
||||
|
||||
|
||||
def test_node_insertion(differ):
|
||||
code1 = dedent('''
|
||||
class X():
|
||||
def y(self):
|
||||
a = 1
|
||||
b = 2
|
||||
|
||||
c = 3
|
||||
d = 4
|
||||
''')
|
||||
code2 = dedent('''
|
||||
class X():
|
||||
def y(self):
|
||||
a = 1
|
||||
b = 2
|
||||
str
|
||||
|
||||
c = 3
|
||||
d = 4
|
||||
''')
|
||||
|
||||
differ.initialize(code1)
|
||||
differ.parse(code2, parsers=1, copies=2)
|
||||
|
||||
Reference in New Issue
Block a user