Towards using an internal buffer for all snippet updates. (#973)
This commit is contained in:
parent
165dd4b3cd
commit
64a023a7ec
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 #}}}
|
||||
|
Loading…
Reference in New Issue
Block a user