1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
|
#!/usr/bin/env python
# encoding: utf-8
"""Some classes to conserve Vim's state for comparing over time."""
from collections import deque, namedtuple
from UltiSnips import vim_helper
from UltiSnips.compatibility import as_unicode, byte2col
from UltiSnips.position import Position
_Placeholder = namedtuple("_FrozenPlaceholder", ["current_text", "start", "end"])
class VimPosition(Position):
"""Represents the current position in the buffer, together with some status
variables that might change our decisions down the line."""
def __init__(self):
pos = vim_helper.buf.cursor
self._mode = vim_helper.eval("mode()")
Position.__init__(self, pos.line, pos.col)
@property
def mode(self):
"""Returns the mode() this position was created."""
return self._mode
class VimState(object):
"""Caches some state information from Vim to better guess what editing
tasks the user might have done in the last step."""
def __init__(self):
self._poss = deque(maxlen=5)
self._lvb = None
self._text_to_expect = ""
self._unnamed_reg_cached = False
# We store the cached value of the unnamed register in Vim directly to
# avoid any Unicode issues with saving and restoring the unnamed
# register across the Python bindings. The unnamed register can contain
# data that cannot be coerced to Unicode, and so a simple vim.eval('@"')
# fails badly. Keeping the cached value in Vim directly, sidesteps the
# problem.
vim_helper.command('let g:_ultisnips_unnamed_reg_cache = ""')
def remember_unnamed_register(self, text_to_expect):
"""Save the unnamed register.
'text_to_expect' is text that we expect
to be contained in the register the next time this method is called -
this could be text from the tabstop that was selected and might have
been overwritten. We will not cache that then.
"""
self._unnamed_reg_cached = True
escaped_text = self._text_to_expect.replace("'", "''")
res = int(vim_helper.eval('@" != ' + "'" + escaped_text + "'"))
if res:
vim_helper.command('let g:_ultisnips_unnamed_reg_cache = @"')
self._text_to_expect = text_to_expect
def restore_unnamed_register(self):
"""Restores the unnamed register and forgets what we cached."""
if not self._unnamed_reg_cached:
return
vim_helper.command('let @" = g:_ultisnips_unnamed_reg_cache')
self._unnamed_reg_cached = False
def remember_position(self):
"""Remember the current position as a previous pose."""
self._poss.append(VimPosition())
def remember_buffer(self, to):
"""Remember the content of the buffer and the position."""
self._lvb = vim_helper.buf[to.start.line : to.end.line + 1]
self._lvb_len = len(vim_helper.buf)
self.remember_position()
@property
def diff_in_buffer_length(self):
"""Returns the difference in the length of the current buffer compared
to the remembered."""
return len(vim_helper.buf) - self._lvb_len
@property
def pos(self):
"""The last remembered position."""
return self._poss[-1]
@property
def ppos(self):
"""The second to last remembered position."""
return self._poss[-2]
@property
def remembered_buffer(self):
"""The content of the remembered buffer."""
return self._lvb[:]
class VisualContentPreserver(object):
"""Saves the current visual selection and the selection mode it was done in
(e.g. line selection, block selection or regular selection.)"""
def __init__(self):
self.reset()
def reset(self):
"""Forget the preserved state."""
self._mode = ""
self._text = as_unicode("")
self._placeholder = None
def conserve(self):
"""Save the last visual selection and the mode it was made in."""
sl, sbyte = map(
int, (vim_helper.eval("""line("'<")"""), vim_helper.eval("""col("'<")"""))
)
el, ebyte = map(
int, (vim_helper.eval("""line("'>")"""), vim_helper.eval("""col("'>")"""))
)
sc = byte2col(sl, sbyte - 1)
ec = byte2col(el, ebyte - 1)
self._mode = vim_helper.eval("visualmode()")
# When 'selection' is 'exclusive', the > mark is one column behind the
# actual content being copied, but never before the < mark.
if vim_helper.eval("&selection") == "exclusive":
if not (sl == el and sbyte == ebyte):
ec -= 1
_vim_line_with_eol = lambda ln: vim_helper.buf[ln] + "\n"
if sl == el:
text = _vim_line_with_eol(sl - 1)[sc : ec + 1]
else:
text = _vim_line_with_eol(sl - 1)[sc:]
for cl in range(sl, el - 1):
text += _vim_line_with_eol(cl)
text += _vim_line_with_eol(el - 1)[: ec + 1]
self._text = text
def conserve_placeholder(self, placeholder):
if placeholder:
self._placeholder = _Placeholder(
placeholder.current_text, placeholder.start, placeholder.end
)
else:
self._placeholder = None
@property
def text(self):
"""The conserved text."""
return self._text
@property
def mode(self):
"""The conserved visualmode()."""
return self._mode
@property
def placeholder(self):
"""Returns latest selected placeholder."""
return self._placeholder
|