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
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def use_proxy_buffer(snippets_stack):
|
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)
|
buffer_proxy = VimBufferProxy(snippets_stack)
|
||||||
old_buffer = _vim.buf
|
old_buffer = _vim.buf
|
||||||
try:
|
try:
|
||||||
@ -20,8 +25,12 @@ def use_proxy_buffer(snippets_stack):
|
|||||||
_vim.buf = old_buffer
|
_vim.buf = old_buffer
|
||||||
buffer_proxy.validate_buffer()
|
buffer_proxy.validate_buffer()
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def suspend_proxy_edits():
|
def suspend_proxy_edits():
|
||||||
|
"""
|
||||||
|
Prevents changes being applied to the snippet stack while function call.
|
||||||
|
"""
|
||||||
if not isinstance(_vim.buf, VimBufferProxy):
|
if not isinstance(_vim.buf, VimBufferProxy):
|
||||||
yield
|
yield
|
||||||
else:
|
else:
|
||||||
@ -31,17 +40,47 @@ def suspend_proxy_edits():
|
|||||||
finally:
|
finally:
|
||||||
_vim.buf._enable_edits()
|
_vim.buf._enable_edits()
|
||||||
|
|
||||||
|
|
||||||
class VimBufferProxy(_vim.VimBuffer):
|
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):
|
def __init__(self, snippets_stack):
|
||||||
|
"""
|
||||||
|
Instantiate new object.
|
||||||
|
|
||||||
|
snippets_stack is a slice of currently active snippets.
|
||||||
|
"""
|
||||||
self._snippets_stack = snippets_stack
|
self._snippets_stack = snippets_stack
|
||||||
self._buffer = vim.current.buffer
|
self._buffer = vim.current.buffer
|
||||||
self._change_tick = int(vim.eval("b:changedtick"))
|
self._change_tick = int(vim.eval("b:changedtick"))
|
||||||
self._forward_edits = True
|
self._forward_edits = True
|
||||||
|
|
||||||
def is_buffer_changed_outside(self):
|
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"))
|
return self._change_tick < int(vim.eval("b:changedtick"))
|
||||||
|
|
||||||
def validate_buffer(self):
|
def validate_buffer(self):
|
||||||
|
"""
|
||||||
|
Raises exception if buffer is changes beyound proxy object.
|
||||||
|
"""
|
||||||
if self.is_buffer_changed_outside():
|
if self.is_buffer_changed_outside():
|
||||||
raise RuntimeError('buffer was modified using vim.command or ' +
|
raise RuntimeError('buffer was modified using vim.command or ' +
|
||||||
'vim.current.buffer; that changes are untrackable and leads to ' +
|
'vim.current.buffer; that changes are untrackable and leads to ' +
|
||||||
@ -49,6 +88,10 @@ class VimBufferProxy(_vim.VimBuffer):
|
|||||||
'for buffer modifications')
|
'for buffer modifications')
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
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):
|
if isinstance(key, slice):
|
||||||
value = [as_vimencoding(l) for l in value]
|
value = [as_vimencoding(l) for l in value]
|
||||||
changes = list(self._get_diff(key.start, key.stop, value))
|
changes = list(self._get_diff(key.start, key.stop, value))
|
||||||
@ -65,21 +108,36 @@ class VimBufferProxy(_vim.VimBuffer):
|
|||||||
self._apply_change(change)
|
self._apply_change(change)
|
||||||
|
|
||||||
def __setslice__(self, i, j, text):
|
def __setslice__(self, i, j, text):
|
||||||
|
"""
|
||||||
|
Same as __setitem__.
|
||||||
|
"""
|
||||||
self.__setitem__(slice(i, j), text)
|
self.__setitem__(slice(i, j), text)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
|
"""
|
||||||
|
Just passing call to the vim.current.window.buffer.__getitem__.
|
||||||
|
"""
|
||||||
if isinstance(key, slice):
|
if isinstance(key, slice):
|
||||||
return [as_unicode(l) for l in self._buffer[key.start:key.stop]]
|
return [as_unicode(l) for l in self._buffer[key.start:key.stop]]
|
||||||
else:
|
else:
|
||||||
return as_unicode(self._buffer[key])
|
return as_unicode(self._buffer[key])
|
||||||
|
|
||||||
def __getslice__(self, i, j):
|
def __getslice__(self, i, j):
|
||||||
|
"""
|
||||||
|
Same as __getitem__.
|
||||||
|
"""
|
||||||
return self.__getitem__(slice(i, j))
|
return self.__getitem__(slice(i, j))
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
|
"""
|
||||||
|
Same as len(vim.current.window.buffer).
|
||||||
|
"""
|
||||||
return len(self._buffer)
|
return len(self._buffer)
|
||||||
|
|
||||||
def append(self, line, line_number=-1):
|
def append(self, line, line_number=-1):
|
||||||
|
"""
|
||||||
|
Same as vim.current.window.buffer.append(), but with tracking changes.
|
||||||
|
"""
|
||||||
if line_number < 0:
|
if line_number < 0:
|
||||||
line_number = len(self)
|
line_number = len(self)
|
||||||
if not isinstance(line, list):
|
if not isinstance(line, list):
|
||||||
@ -93,6 +151,9 @@ class VimBufferProxy(_vim.VimBuffer):
|
|||||||
self.__setitem__(slice(key, key+1), [])
|
self.__setitem__(slice(key, key+1), [])
|
||||||
|
|
||||||
def _get_diff(self, start, end, new_value):
|
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):
|
for line_number in range(start, end):
|
||||||
yield ('D', line_number, 0, self._buffer[line_number])
|
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])
|
yield ('I', start+line_number, 0, new_value[line_number])
|
||||||
|
|
||||||
def _get_line_diff(self, line_number, before, after):
|
def _get_line_diff(self, line_number, before, after):
|
||||||
|
"""
|
||||||
|
Use precise diffing for tracking changes in single line.
|
||||||
|
"""
|
||||||
if before == '':
|
if before == '':
|
||||||
for change in self._get_diff(line_number, line_number+1, [after]):
|
for change in self._get_diff(line_number, line_number+1, [after]):
|
||||||
yield change
|
yield change
|
||||||
@ -108,6 +172,10 @@ class VimBufferProxy(_vim.VimBuffer):
|
|||||||
yield (change[0], line_number, change[2], change[3])
|
yield (change[0], line_number, change[2], change[3])
|
||||||
|
|
||||||
def _apply_change(self, change):
|
def _apply_change(self, change):
|
||||||
|
"""
|
||||||
|
Apply changeset to current snippets stack, correctly moving around
|
||||||
|
snippet itself or its child.
|
||||||
|
"""
|
||||||
if not self._snippets_stack:
|
if not self._snippets_stack:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -128,7 +196,15 @@ class VimBufferProxy(_vim.VimBuffer):
|
|||||||
self._snippets_stack[0]._do_edit(change)
|
self._snippets_stack[0]._do_edit(change)
|
||||||
|
|
||||||
def _disable_edits(self):
|
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
|
self._forward_edits = False
|
||||||
|
|
||||||
def _enable_edits(self):
|
def _enable_edits(self):
|
||||||
|
"""
|
||||||
|
Enables changes forwarding back.
|
||||||
|
"""
|
||||||
self._forward_edits = True
|
self._forward_edits = True
|
Loading…
x
Reference in New Issue
Block a user