add docs to the buffer proxy object
This commit is contained in:
parent
bc29e23226
commit
8958b71341
@ -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
|
Loading…
x
Reference in New Issue
Block a user