Towards using an internal buffer for all snippet updates. (#973)

This commit is contained in:
Holger Rapp 2018-04-14 17:59:48 +02:00 committed by UltiBot
parent 165dd4b3cd
commit 64a023a7ec
11 changed files with 78 additions and 83 deletions

View File

@ -75,8 +75,8 @@ class VimBuffer(object):
buf = VimBuffer() # pylint:disable=invalid-name
@contextmanager
def toggle_opt(name, new_value):
old_value = eval('&' + name)
def option_set_to(name, new_value):
old_value = vim.eval('&' + name)
command('set {0}={1}'.format(name, new_value))
try:
yield

View File

@ -13,12 +13,47 @@ from UltiSnips.compatibility import as_unicode
from UltiSnips.indent_util import IndentUtil
from UltiSnips.text import escape
from UltiSnips.text_objects import SnippetInstance
from UltiSnips.text_objects._python_code import \
SnippetUtilCursor, SnippetUtilForAction
from UltiSnips.text_objects._python_code import SnippetUtilForAction
__WHITESPACE_SPLIT = re.compile(r"\s")
class _SnippetUtilCursor(object):
def __init__(self, cursor):
self._cursor = [cursor[0] - 1, cursor[1]]
self._set = False
def preserve(self):
self._set = True
self._cursor = [
_vim.buf.cursor[0],
_vim.buf.cursor[1],
]
def is_set(self):
return self._set
def set(self, line, column):
self.__setitem__(0, line)
self.__setitem__(1, column)
def to_vim_cursor(self):
return (self._cursor[0] + 1, self._cursor[1])
def __getitem__(self, index):
return self._cursor[index]
def __setitem__(self, index, value):
self._set = True
self._cursor[index] = value
def __len__(self):
return 2
def __str__(self):
return str((self._cursor[0], self._cursor[1]))
def split_at_whitespace(string):
"""Like string.split(), but keeps empty words as empty words."""
return re.split(__WHITESPACE_SPLIT, string)
@ -124,7 +159,7 @@ class SnippetDefinition(object):
'buffer': current.buffer,
'line': current.window.cursor[0]-1,
'column': current.window.cursor[1]-1,
'cursor': SnippetUtilCursor(current.window.cursor),
'cursor': _SnippetUtilCursor(current.window.cursor),
}
locals.update(additional_locals)
@ -438,6 +473,6 @@ class SnippetDefinition(object):
last_re=self._last_re, globals=self._globals,
context=self._context)
self.instantiate(snippet_instance, initial_text, indent)
snippet_instance.replace_initial_text()
snippet_instance.update_textobjects()
snippet_instance.replace_initial_text(_vim.buf)
snippet_instance.update_textobjects(_vim.buf)
return snippet_instance

View File

@ -336,7 +336,7 @@ class SnippetManager(object):
self._check_if_still_inside_snippet()
if self._csnippets:
self._csnippets[0].update_textobjects()
self._csnippets[0].update_textobjects(_vim.buf)
self._vstate.remember_buffer(self._csnippets[0])
def _setup_inner_state(self):
@ -448,7 +448,7 @@ class SnippetManager(object):
# we need to set 'onemore' there, because of limitations of the vim
# API regarding cursor movements; without that test
# 'CanExpandAnonSnippetInJumpActionWhileSelected' will fail
with _vim.toggle_opt('ve', 'onemore'):
with _vim.option_set_to('ve', 'onemore'):
jumped = False
# We need to remember current snippets stack here because of
@ -489,7 +489,7 @@ class SnippetManager(object):
self._cs.current_placeholder = \
self._visual_content.placeholder
self._should_reset_visual = False
self._csnippets[0].update_textobjects()
self._csnippets[0].update_textobjects(_vim.buf)
self._vstate.remember_buffer(self._csnippets[0])
if ntab.number == 0 and self._csnippets:

View File

@ -15,25 +15,26 @@ def _calc_end(text, start):
new_end = Position(start.line + len(text) - 1, len(text[-1]))
return new_end
def _text_to_vim(start, end, text):
def _replace_text(buf, start, end, text):
"""Copy the given text to the current buffer, overwriting the span 'start'
to 'end'."""
lines = text.split('\n')
new_end = _calc_end(lines, start)
before = _vim.buf[start.line][:start.col]
after = _vim.buf[end.line][end.col:]
before = buf[start.line][:start.col]
after = buf[end.line][end.col:]
new_lines = []
if len(lines):
new_lines.append(before + lines[0])
new_lines.extend(lines[1:])
new_lines[-1] += after
_vim.buf[start.line:end.line + 1] = new_lines
buf[start.line:end.line + 1] = new_lines
# Open any folds this might have created
# TODO(sirver): This leaks that we are still inside Vim, while this code should
# only care that it is modifying 'buf'.
_vim.buf.cursor = start
_vim.command('normal! zv')
@ -115,10 +116,10 @@ class TextObject(object):
"""The end position."""
return self._end
def overwrite_with_initial_text(self):
self.overwrite(self._initial_text)
def overwrite_with_initial_text(self, buf):
self.overwrite(buf, self._initial_text)
def overwrite(self, gtext):
def overwrite(self, buf, gtext):
"""Overwrite the text of this object in the Vim Buffer and update its
length information.
@ -131,16 +132,16 @@ class TextObject(object):
if self.current_text == gtext:
return
old_end = self._end
self._end = _text_to_vim(
self._start, self._end, gtext)
self._end = _replace_text(
buf, self._start, self._end, gtext)
if self._parent:
self._parent._child_has_moved(
self._parent._children.index(self), min(old_end, self._end),
self._end.delta(old_end)
)
def _update(self, done):
"""Update this object inside the Vim Buffer.
def _update(self, done, buf):
"""Update this object inside 'buf' which is a list of lines.
Return False if you need to be called again for this edit cycle.
Otherwise return True.
@ -357,7 +358,7 @@ class EditableTextObject(TextObject):
if self._parent and requester is not self._parent:
return self._parent._get_tabstop(self, number)
def _update(self, done):
def _update(self, done, buf):
if all((child in done) for child in self._children):
assert self not in done
done.add(self)
@ -385,5 +386,5 @@ class NoneditableTextObject(TextObject):
"""All passive text objects that the user can't edit by hand."""
def _update(self, done):
def _update(self, done, buf):
return True

View File

@ -14,16 +14,16 @@ class Mirror(NoneditableTextObject):
NoneditableTextObject.__init__(self, parent, token)
self._ts = tabstop
def _update(self, done):
def _update(self, done, buf):
if self._ts.is_killed:
self.overwrite('')
self.overwrite(buf, '')
self._parent._del_child(self) # pylint:disable=protected-access
return True
if self._ts not in done:
return False
self.overwrite(self._get_text())
self.overwrite(buf, self._get_text())
return True
def _get_text(self):

View File

@ -35,7 +35,8 @@ class _Tabs(object):
int(no)) # pylint:disable=protected-access
if ts is None:
return
ts.overwrite(value)
# TODO(sirver): The buffer should be passed into the object on construction.
ts.overwrite(_vim.buf, value)
_VisualContent = namedtuple('_VisualContent', ['mode', 'text'])
@ -52,42 +53,6 @@ class SnippetUtilForAction(dict):
self.cursor.preserve()
class SnippetUtilCursor(object):
def __init__(self, cursor):
self._cursor = [cursor[0] - 1, cursor[1]]
self._set = False
def preserve(self):
self._set = True
self._cursor = [
_vim.buf.cursor[0],
_vim.buf.cursor[1],
]
def is_set(self):
return self._set
def set(self, line, column):
self.__setitem__(0, line)
self.__setitem__(1, column)
def to_vim_cursor(self):
return (self._cursor[0] + 1, self._cursor[1])
def __getitem__(self, index):
return self._cursor[index]
def __setitem__(self, index, value):
self._set = True
self._cursor[index] = value
def __len__(self):
return 2
def __str__(self):
return str((self._cursor[0], self._cursor[1]))
class SnippetUtil(object):
"""Provides easy access to indentation, etc.
@ -290,7 +255,7 @@ class PythonCode(NoneditableTextObject):
))
NoneditableTextObject.__init__(self, parent, token)
def _update(self, done):
def _update(self, done, buf):
path = _vim.eval('expand("%")') or ''
ct = self.current_text
self._locals.update({
@ -316,6 +281,6 @@ class PythonCode(NoneditableTextObject):
)
if ct != rv:
self.overwrite(rv)
self.overwrite(buf, rv)
return False
return True

View File

@ -65,12 +65,12 @@ class ShellCode(NoneditableTextObject):
self._code = token.code.replace('\\`', '`')
self._tmpdir = _get_tmp()
def _update(self, done):
def _update(self, done, buf):
if not self._tmpdir:
output = \
'Unable to find executable tmp directory, check noexec on /tmp'
else:
output = _run_shell_command(self._code, self._tmpdir)
self.overwrite(output)
self.overwrite(buf, output)
self._parent._del_child(self) # pylint:disable=protected-access
return True

View File

@ -38,11 +38,11 @@ class SnippetInstance(EditableTextObject):
EditableTextObject.__init__(self, parent, start, end, initial_text)
def replace_initial_text(self):
def replace_initial_text(self, buf):
"""Puts the initial text of all text elements into Vim."""
def _place_initial_text(obj):
"""recurses on the children to do the work."""
obj.overwrite_with_initial_text()
obj.overwrite_with_initial_text(buf)
if isinstance(obj, EditableTextObject):
for child in obj._children:
_place_initial_text(child)
@ -54,7 +54,7 @@ class SnippetInstance(EditableTextObject):
for cmd in cmds:
self._do_edit(cmd, ctab)
def update_textobjects(self):
def update_textobjects(self, buf):
"""Update the text objects that should change automagically after the
users edits have been replayed.
@ -77,7 +77,7 @@ class SnippetInstance(EditableTextObject):
while (done != not_done) and counter:
# Order matters for python locals!
for obj in sorted(not_done - done):
if obj._update(done):
if obj._update(done, buf):
done.add(obj)
counter -= 1
if not counter:

View File

@ -16,6 +16,6 @@ class VimLCode(NoneditableTextObject):
NoneditableTextObject.__init__(self, parent, token)
def _update(self, done):
self.overwrite(_vim.eval(self._code))
def _update(self, done, buf):
self.overwrite(buf, _vim.eval(self._code))
return True

View File

@ -11,7 +11,6 @@ If there was no text visually selected, this will be the empty string.
import re
import textwrap
from UltiSnips import _vim
from UltiSnips.indent_util import IndentUtil
from UltiSnips.text_objects._transformation import TextObjectTransformation
from UltiSnips.text_objects._base import NoneditableTextObject
@ -40,11 +39,11 @@ class Visual(NoneditableTextObject, TextObjectTransformation):
NoneditableTextObject.__init__(self, parent, token)
TextObjectTransformation.__init__(self, token)
def _update(self, done):
def _update(self, done, buf):
if self._mode == 'v': # Normal selection.
text = self._text
else: # Block selection or line selection.
text_before = _vim.buf[self.start.line][:self.start.col]
text_before = buf[self.start.line][:self.start.col]
indent = _REPLACE_NON_WS.sub(' ', text_before)
iu = IndentUtil()
indent = iu.indent_to_spaces(indent)
@ -58,7 +57,7 @@ class Visual(NoneditableTextObject, TextObjectTransformation):
text = text[:-1] # Strip final '\n'
text = self._transform(text)
self.overwrite(text)
self.overwrite(buf, text)
self._parent._del_child(self) # pylint:disable=protected-access
return True

View File

@ -1,9 +1,6 @@
from test.vim_test_case import VimTestCase as _VimTest
from test.constant import *
# Mirrors {{{#
class TextTabStopTextAfterTab_ExpectCorrectResult(_VimTest):
snippets = ('test', '$1 Hinten\n$1')
keys = 'test' + EX + 'hallo'
@ -268,5 +265,3 @@ class Mirror_TestKillTabstop_Kill(_VimTest):
snippets = 'test', 'welt${1:welt${2:welt}welt} $2'
keys = 'hallo test' + EX + 'elt'
wanted = 'hallo weltelt '
# End: Mirrors #}}}