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
|
buf = VimBuffer() # pylint:disable=invalid-name
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def toggle_opt(name, new_value):
|
def option_set_to(name, new_value):
|
||||||
old_value = eval('&' + name)
|
old_value = vim.eval('&' + name)
|
||||||
command('set {0}={1}'.format(name, new_value))
|
command('set {0}={1}'.format(name, new_value))
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
|
@ -13,12 +13,47 @@ from UltiSnips.compatibility import as_unicode
|
|||||||
from UltiSnips.indent_util import IndentUtil
|
from UltiSnips.indent_util import IndentUtil
|
||||||
from UltiSnips.text import escape
|
from UltiSnips.text import escape
|
||||||
from UltiSnips.text_objects import SnippetInstance
|
from UltiSnips.text_objects import SnippetInstance
|
||||||
from UltiSnips.text_objects._python_code import \
|
from UltiSnips.text_objects._python_code import SnippetUtilForAction
|
||||||
SnippetUtilCursor, SnippetUtilForAction
|
|
||||||
|
|
||||||
__WHITESPACE_SPLIT = re.compile(r"\s")
|
__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):
|
def split_at_whitespace(string):
|
||||||
"""Like string.split(), but keeps empty words as empty words."""
|
"""Like string.split(), but keeps empty words as empty words."""
|
||||||
return re.split(__WHITESPACE_SPLIT, string)
|
return re.split(__WHITESPACE_SPLIT, string)
|
||||||
@ -124,7 +159,7 @@ class SnippetDefinition(object):
|
|||||||
'buffer': current.buffer,
|
'buffer': current.buffer,
|
||||||
'line': current.window.cursor[0]-1,
|
'line': current.window.cursor[0]-1,
|
||||||
'column': current.window.cursor[1]-1,
|
'column': current.window.cursor[1]-1,
|
||||||
'cursor': SnippetUtilCursor(current.window.cursor),
|
'cursor': _SnippetUtilCursor(current.window.cursor),
|
||||||
}
|
}
|
||||||
|
|
||||||
locals.update(additional_locals)
|
locals.update(additional_locals)
|
||||||
@ -438,6 +473,6 @@ class SnippetDefinition(object):
|
|||||||
last_re=self._last_re, globals=self._globals,
|
last_re=self._last_re, globals=self._globals,
|
||||||
context=self._context)
|
context=self._context)
|
||||||
self.instantiate(snippet_instance, initial_text, indent)
|
self.instantiate(snippet_instance, initial_text, indent)
|
||||||
snippet_instance.replace_initial_text()
|
snippet_instance.replace_initial_text(_vim.buf)
|
||||||
snippet_instance.update_textobjects()
|
snippet_instance.update_textobjects(_vim.buf)
|
||||||
return snippet_instance
|
return snippet_instance
|
||||||
|
@ -336,7 +336,7 @@ class SnippetManager(object):
|
|||||||
|
|
||||||
self._check_if_still_inside_snippet()
|
self._check_if_still_inside_snippet()
|
||||||
if self._csnippets:
|
if self._csnippets:
|
||||||
self._csnippets[0].update_textobjects()
|
self._csnippets[0].update_textobjects(_vim.buf)
|
||||||
self._vstate.remember_buffer(self._csnippets[0])
|
self._vstate.remember_buffer(self._csnippets[0])
|
||||||
|
|
||||||
def _setup_inner_state(self):
|
def _setup_inner_state(self):
|
||||||
@ -448,7 +448,7 @@ class SnippetManager(object):
|
|||||||
# we need to set 'onemore' there, because of limitations of the vim
|
# we need to set 'onemore' there, because of limitations of the vim
|
||||||
# API regarding cursor movements; without that test
|
# API regarding cursor movements; without that test
|
||||||
# 'CanExpandAnonSnippetInJumpActionWhileSelected' will fail
|
# 'CanExpandAnonSnippetInJumpActionWhileSelected' will fail
|
||||||
with _vim.toggle_opt('ve', 'onemore'):
|
with _vim.option_set_to('ve', 'onemore'):
|
||||||
jumped = False
|
jumped = False
|
||||||
|
|
||||||
# We need to remember current snippets stack here because of
|
# We need to remember current snippets stack here because of
|
||||||
@ -489,7 +489,7 @@ class SnippetManager(object):
|
|||||||
self._cs.current_placeholder = \
|
self._cs.current_placeholder = \
|
||||||
self._visual_content.placeholder
|
self._visual_content.placeholder
|
||||||
self._should_reset_visual = False
|
self._should_reset_visual = False
|
||||||
self._csnippets[0].update_textobjects()
|
self._csnippets[0].update_textobjects(_vim.buf)
|
||||||
self._vstate.remember_buffer(self._csnippets[0])
|
self._vstate.remember_buffer(self._csnippets[0])
|
||||||
|
|
||||||
if ntab.number == 0 and self._csnippets:
|
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]))
|
new_end = Position(start.line + len(text) - 1, len(text[-1]))
|
||||||
return new_end
|
return new_end
|
||||||
|
|
||||||
|
def _replace_text(buf, start, end, text):
|
||||||
def _text_to_vim(start, end, text):
|
|
||||||
"""Copy the given text to the current buffer, overwriting the span 'start'
|
"""Copy the given text to the current buffer, overwriting the span 'start'
|
||||||
to 'end'."""
|
to 'end'."""
|
||||||
lines = text.split('\n')
|
lines = text.split('\n')
|
||||||
|
|
||||||
new_end = _calc_end(lines, start)
|
new_end = _calc_end(lines, start)
|
||||||
|
|
||||||
before = _vim.buf[start.line][:start.col]
|
before = buf[start.line][:start.col]
|
||||||
after = _vim.buf[end.line][end.col:]
|
after = buf[end.line][end.col:]
|
||||||
|
|
||||||
new_lines = []
|
new_lines = []
|
||||||
if len(lines):
|
if len(lines):
|
||||||
new_lines.append(before + lines[0])
|
new_lines.append(before + lines[0])
|
||||||
new_lines.extend(lines[1:])
|
new_lines.extend(lines[1:])
|
||||||
new_lines[-1] += after
|
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
|
# 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.buf.cursor = start
|
||||||
_vim.command('normal! zv')
|
_vim.command('normal! zv')
|
||||||
|
|
||||||
@ -115,10 +116,10 @@ class TextObject(object):
|
|||||||
"""The end position."""
|
"""The end position."""
|
||||||
return self._end
|
return self._end
|
||||||
|
|
||||||
def overwrite_with_initial_text(self):
|
def overwrite_with_initial_text(self, buf):
|
||||||
self.overwrite(self._initial_text)
|
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
|
"""Overwrite the text of this object in the Vim Buffer and update its
|
||||||
length information.
|
length information.
|
||||||
|
|
||||||
@ -131,16 +132,16 @@ class TextObject(object):
|
|||||||
if self.current_text == gtext:
|
if self.current_text == gtext:
|
||||||
return
|
return
|
||||||
old_end = self._end
|
old_end = self._end
|
||||||
self._end = _text_to_vim(
|
self._end = _replace_text(
|
||||||
self._start, self._end, gtext)
|
buf, self._start, self._end, gtext)
|
||||||
if self._parent:
|
if self._parent:
|
||||||
self._parent._child_has_moved(
|
self._parent._child_has_moved(
|
||||||
self._parent._children.index(self), min(old_end, self._end),
|
self._parent._children.index(self), min(old_end, self._end),
|
||||||
self._end.delta(old_end)
|
self._end.delta(old_end)
|
||||||
)
|
)
|
||||||
|
|
||||||
def _update(self, done):
|
def _update(self, done, buf):
|
||||||
"""Update this object inside the Vim Buffer.
|
"""Update this object inside 'buf' which is a list of lines.
|
||||||
|
|
||||||
Return False if you need to be called again for this edit cycle.
|
Return False if you need to be called again for this edit cycle.
|
||||||
Otherwise return True.
|
Otherwise return True.
|
||||||
@ -357,7 +358,7 @@ class EditableTextObject(TextObject):
|
|||||||
if self._parent and requester is not self._parent:
|
if self._parent and requester is not self._parent:
|
||||||
return self._parent._get_tabstop(self, number)
|
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):
|
if all((child in done) for child in self._children):
|
||||||
assert self not in done
|
assert self not in done
|
||||||
done.add(self)
|
done.add(self)
|
||||||
@ -385,5 +386,5 @@ class NoneditableTextObject(TextObject):
|
|||||||
|
|
||||||
"""All passive text objects that the user can't edit by hand."""
|
"""All passive text objects that the user can't edit by hand."""
|
||||||
|
|
||||||
def _update(self, done):
|
def _update(self, done, buf):
|
||||||
return True
|
return True
|
||||||
|
@ -14,16 +14,16 @@ class Mirror(NoneditableTextObject):
|
|||||||
NoneditableTextObject.__init__(self, parent, token)
|
NoneditableTextObject.__init__(self, parent, token)
|
||||||
self._ts = tabstop
|
self._ts = tabstop
|
||||||
|
|
||||||
def _update(self, done):
|
def _update(self, done, buf):
|
||||||
if self._ts.is_killed:
|
if self._ts.is_killed:
|
||||||
self.overwrite('')
|
self.overwrite(buf, '')
|
||||||
self._parent._del_child(self) # pylint:disable=protected-access
|
self._parent._del_child(self) # pylint:disable=protected-access
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if self._ts not in done:
|
if self._ts not in done:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.overwrite(self._get_text())
|
self.overwrite(buf, self._get_text())
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _get_text(self):
|
def _get_text(self):
|
||||||
|
@ -35,7 +35,8 @@ class _Tabs(object):
|
|||||||
int(no)) # pylint:disable=protected-access
|
int(no)) # pylint:disable=protected-access
|
||||||
if ts is None:
|
if ts is None:
|
||||||
return
|
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'])
|
_VisualContent = namedtuple('_VisualContent', ['mode', 'text'])
|
||||||
|
|
||||||
@ -52,42 +53,6 @@ class SnippetUtilForAction(dict):
|
|||||||
self.cursor.preserve()
|
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):
|
class SnippetUtil(object):
|
||||||
|
|
||||||
"""Provides easy access to indentation, etc.
|
"""Provides easy access to indentation, etc.
|
||||||
@ -290,7 +255,7 @@ class PythonCode(NoneditableTextObject):
|
|||||||
))
|
))
|
||||||
NoneditableTextObject.__init__(self, parent, token)
|
NoneditableTextObject.__init__(self, parent, token)
|
||||||
|
|
||||||
def _update(self, done):
|
def _update(self, done, buf):
|
||||||
path = _vim.eval('expand("%")') or ''
|
path = _vim.eval('expand("%")') or ''
|
||||||
ct = self.current_text
|
ct = self.current_text
|
||||||
self._locals.update({
|
self._locals.update({
|
||||||
@ -316,6 +281,6 @@ class PythonCode(NoneditableTextObject):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if ct != rv:
|
if ct != rv:
|
||||||
self.overwrite(rv)
|
self.overwrite(buf, rv)
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
@ -65,12 +65,12 @@ class ShellCode(NoneditableTextObject):
|
|||||||
self._code = token.code.replace('\\`', '`')
|
self._code = token.code.replace('\\`', '`')
|
||||||
self._tmpdir = _get_tmp()
|
self._tmpdir = _get_tmp()
|
||||||
|
|
||||||
def _update(self, done):
|
def _update(self, done, buf):
|
||||||
if not self._tmpdir:
|
if not self._tmpdir:
|
||||||
output = \
|
output = \
|
||||||
'Unable to find executable tmp directory, check noexec on /tmp'
|
'Unable to find executable tmp directory, check noexec on /tmp'
|
||||||
else:
|
else:
|
||||||
output = _run_shell_command(self._code, self._tmpdir)
|
output = _run_shell_command(self._code, self._tmpdir)
|
||||||
self.overwrite(output)
|
self.overwrite(buf, output)
|
||||||
self._parent._del_child(self) # pylint:disable=protected-access
|
self._parent._del_child(self) # pylint:disable=protected-access
|
||||||
return True
|
return True
|
||||||
|
@ -38,11 +38,11 @@ class SnippetInstance(EditableTextObject):
|
|||||||
|
|
||||||
EditableTextObject.__init__(self, parent, start, end, initial_text)
|
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."""
|
"""Puts the initial text of all text elements into Vim."""
|
||||||
def _place_initial_text(obj):
|
def _place_initial_text(obj):
|
||||||
"""recurses on the children to do the work."""
|
"""recurses on the children to do the work."""
|
||||||
obj.overwrite_with_initial_text()
|
obj.overwrite_with_initial_text(buf)
|
||||||
if isinstance(obj, EditableTextObject):
|
if isinstance(obj, EditableTextObject):
|
||||||
for child in obj._children:
|
for child in obj._children:
|
||||||
_place_initial_text(child)
|
_place_initial_text(child)
|
||||||
@ -54,7 +54,7 @@ class SnippetInstance(EditableTextObject):
|
|||||||
for cmd in cmds:
|
for cmd in cmds:
|
||||||
self._do_edit(cmd, ctab)
|
self._do_edit(cmd, ctab)
|
||||||
|
|
||||||
def update_textobjects(self):
|
def update_textobjects(self, buf):
|
||||||
"""Update the text objects that should change automagically after the
|
"""Update the text objects that should change automagically after the
|
||||||
users edits have been replayed.
|
users edits have been replayed.
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ class SnippetInstance(EditableTextObject):
|
|||||||
while (done != not_done) and counter:
|
while (done != not_done) and counter:
|
||||||
# Order matters for python locals!
|
# Order matters for python locals!
|
||||||
for obj in sorted(not_done - done):
|
for obj in sorted(not_done - done):
|
||||||
if obj._update(done):
|
if obj._update(done, buf):
|
||||||
done.add(obj)
|
done.add(obj)
|
||||||
counter -= 1
|
counter -= 1
|
||||||
if not counter:
|
if not counter:
|
||||||
|
@ -16,6 +16,6 @@ class VimLCode(NoneditableTextObject):
|
|||||||
|
|
||||||
NoneditableTextObject.__init__(self, parent, token)
|
NoneditableTextObject.__init__(self, parent, token)
|
||||||
|
|
||||||
def _update(self, done):
|
def _update(self, done, buf):
|
||||||
self.overwrite(_vim.eval(self._code))
|
self.overwrite(buf, _vim.eval(self._code))
|
||||||
return True
|
return True
|
||||||
|
@ -11,7 +11,6 @@ If there was no text visually selected, this will be the empty string.
|
|||||||
import re
|
import re
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
from UltiSnips import _vim
|
|
||||||
from UltiSnips.indent_util import IndentUtil
|
from UltiSnips.indent_util import IndentUtil
|
||||||
from UltiSnips.text_objects._transformation import TextObjectTransformation
|
from UltiSnips.text_objects._transformation import TextObjectTransformation
|
||||||
from UltiSnips.text_objects._base import NoneditableTextObject
|
from UltiSnips.text_objects._base import NoneditableTextObject
|
||||||
@ -40,11 +39,11 @@ class Visual(NoneditableTextObject, TextObjectTransformation):
|
|||||||
NoneditableTextObject.__init__(self, parent, token)
|
NoneditableTextObject.__init__(self, parent, token)
|
||||||
TextObjectTransformation.__init__(self, token)
|
TextObjectTransformation.__init__(self, token)
|
||||||
|
|
||||||
def _update(self, done):
|
def _update(self, done, buf):
|
||||||
if self._mode == 'v': # Normal selection.
|
if self._mode == 'v': # Normal selection.
|
||||||
text = self._text
|
text = self._text
|
||||||
else: # Block selection or line selection.
|
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)
|
indent = _REPLACE_NON_WS.sub(' ', text_before)
|
||||||
iu = IndentUtil()
|
iu = IndentUtil()
|
||||||
indent = iu.indent_to_spaces(indent)
|
indent = iu.indent_to_spaces(indent)
|
||||||
@ -58,7 +57,7 @@ class Visual(NoneditableTextObject, TextObjectTransformation):
|
|||||||
text = text[:-1] # Strip final '\n'
|
text = text[:-1] # Strip final '\n'
|
||||||
|
|
||||||
text = self._transform(text)
|
text = self._transform(text)
|
||||||
self.overwrite(text)
|
self.overwrite(buf, text)
|
||||||
self._parent._del_child(self) # pylint:disable=protected-access
|
self._parent._del_child(self) # pylint:disable=protected-access
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
from test.vim_test_case import VimTestCase as _VimTest
|
from test.vim_test_case import VimTestCase as _VimTest
|
||||||
from test.constant import *
|
from test.constant import *
|
||||||
|
|
||||||
# Mirrors {{{#
|
|
||||||
|
|
||||||
|
|
||||||
class TextTabStopTextAfterTab_ExpectCorrectResult(_VimTest):
|
class TextTabStopTextAfterTab_ExpectCorrectResult(_VimTest):
|
||||||
snippets = ('test', '$1 Hinten\n$1')
|
snippets = ('test', '$1 Hinten\n$1')
|
||||||
keys = 'test' + EX + 'hallo'
|
keys = 'test' + EX + 'hallo'
|
||||||
@ -268,5 +265,3 @@ class Mirror_TestKillTabstop_Kill(_VimTest):
|
|||||||
snippets = 'test', 'welt${1:welt${2:welt}welt} $2'
|
snippets = 'test', 'welt${1:welt${2:welt}welt} $2'
|
||||||
keys = 'hallo test' + EX + 'elt'
|
keys = 'hallo test' + EX + 'elt'
|
||||||
wanted = 'hallo weltelt '
|
wanted = 'hallo weltelt '
|
||||||
|
|
||||||
# End: Mirrors #}}}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user