add docs to the buffer proxy object

This commit is contained in:
Stanislav Seletskiy 2015-06-16 10:19:08 +06:00
parent bc29e23226
commit 8958b71341

View File

@ -9,8 +9,13 @@ from UltiSnips import _vim
from contextlib import contextmanager
@contextmanager
def use_proxy_buffer(snippets_stack):
"""
Forward all changes made in the buffer to the current snippet stack while
function call.
"""
buffer_proxy = VimBufferProxy(snippets_stack)
old_buffer = _vim.buf
try:
@ -20,8 +25,12 @@ def use_proxy_buffer(snippets_stack):
_vim.buf = old_buffer
buffer_proxy.validate_buffer()
@contextmanager
def suspend_proxy_edits():
"""
Prevents changes being applied to the snippet stack while function call.
"""
if not isinstance(_vim.buf, VimBufferProxy):
yield
else:
@ -31,17 +40,47 @@ def suspend_proxy_edits():
finally:
_vim.buf._enable_edits()
class VimBufferProxy(_vim.VimBuffer):
"""
Proxy object used for tracking changes that made from snippet actions.
Unfortunately, vim by itself lacks of the API for changing text in
trackable maner.
Vim marks offers limited functionality for tracking line additions and
deletions, but nothing offered for tracking changes withing single line.
Instance of this class is passed to all snippet actions and behaves as
internal vim.current.window.buffer.
All changes that are made by user passed to diff algorithm, and resulting
diff applied to internal snippet structures to ensure they are in sync with
actual buffer contents.
"""
def __init__(self, snippets_stack):
"""
Instantiate new object.
snippets_stack is a slice of currently active snippets.
"""
self._snippets_stack = snippets_stack
self._buffer = vim.current.buffer
self._change_tick = int(vim.eval("b:changedtick"))
self._forward_edits = True
def is_buffer_changed_outside(self):
"""
Returns true, if buffer was changed without using proxy object, like
with vim.command() or through internal vim.current.window.buffer.
"""
return self._change_tick < int(vim.eval("b:changedtick"))
def validate_buffer(self):
"""
Raises exception if buffer is changes beyound proxy object.
"""
if self.is_buffer_changed_outside():
raise RuntimeError('buffer was modified using vim.command or ' +
'vim.current.buffer; that changes are untrackable and leads to ' +
@ -49,6 +88,10 @@ class VimBufferProxy(_vim.VimBuffer):
'for buffer modifications')
def __setitem__(self, key, value):
"""
Behaves as vim.current.window.buffer.__setitem__ except it tracks
changes and applies them to the current snippet stack.
"""
if isinstance(key, slice):
value = [as_vimencoding(l) for l in value]
changes = list(self._get_diff(key.start, key.stop, value))
@ -65,21 +108,36 @@ class VimBufferProxy(_vim.VimBuffer):
self._apply_change(change)
def __setslice__(self, i, j, text):
"""
Same as __setitem__.
"""
self.__setitem__(slice(i, j), text)
def __getitem__(self, key):
"""
Just passing call to the vim.current.window.buffer.__getitem__.
"""
if isinstance(key, slice):
return [as_unicode(l) for l in self._buffer[key.start:key.stop]]
else:
return as_unicode(self._buffer[key])
def __getslice__(self, i, j):
"""
Same as __getitem__.
"""
return self.__getitem__(slice(i, j))
def __len__(self):
"""
Same as len(vim.current.window.buffer).
"""
return len(self._buffer)
def append(self, line, line_number=-1):
"""
Same as vim.current.window.buffer.append(), but with tracking changes.
"""
if line_number < 0:
line_number = len(self)
if not isinstance(line, list):
@ -93,6 +151,9 @@ class VimBufferProxy(_vim.VimBuffer):
self.__setitem__(slice(key, key+1), [])
def _get_diff(self, start, end, new_value):
"""
Very fast diffing algorithm when changes are across many lines.
"""
for line_number in range(start, end):
yield ('D', line_number, 0, self._buffer[line_number])
@ -100,6 +161,9 @@ class VimBufferProxy(_vim.VimBuffer):
yield ('I', start+line_number, 0, new_value[line_number])
def _get_line_diff(self, line_number, before, after):
"""
Use precise diffing for tracking changes in single line.
"""
if before == '':
for change in self._get_diff(line_number, line_number+1, [after]):
yield change
@ -108,6 +172,10 @@ class VimBufferProxy(_vim.VimBuffer):
yield (change[0], line_number, change[2], change[3])
def _apply_change(self, change):
"""
Apply changeset to current snippets stack, correctly moving around
snippet itself or its child.
"""
if not self._snippets_stack:
return
@ -128,7 +196,15 @@ class VimBufferProxy(_vim.VimBuffer):
self._snippets_stack[0]._do_edit(change)
def _disable_edits(self):
"""
Temporary disable applying changes to snippets stack. Should be done
while expanding anonymous snippet in the middle of jump to prevent
double tracking.
"""
self._forward_edits = False
def _enable_edits(self):
"""
Enables changes forwarding back.
"""
self._forward_edits = True