File: vim_state.py

package info (click to toggle)
vim-ultisnips 3.2-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 4,924 kB
  • sloc: python: 8,353; sh: 64; makefile: 38
file content (170 lines) | stat: -rw-r--r-- 5,655 bytes parent folder | download | duplicates (2)
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