Completed
Pull Request — master (#325)
by Björn
28:01 queued 03:01
created

Buffer.update_highlights()   A

Complexity

Conditions 3

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
dl 0
loc 16
ccs 6
cts 6
cp 1
crap 3
rs 9.4285
c 0
b 0
f 0
1
"""API for working with a Nvim Buffer."""
2 5
from .common import Remote
3 5
from ..compat import IS_PYTHON3, check_async
4
5
6 5
__all__ = ('Buffer')
7
8
9 5
if IS_PYTHON3:
10 3
    basestring = str
11
12
13 5
def adjust_index(idx, default=None):
14
    """Convert from python indexing convention to nvim indexing convention."""
15 5
    if idx is None:
16 5
        return default
17 5
    elif idx < 0:
18 5
        return idx - 1
19
    else:
20 5
        return idx
21
22
23 5
class Buffer(Remote):
24
25
    """A remote Nvim buffer."""
26
27 5
    _api_prefix = "nvim_buf_"
28
29 5
    def __len__(self):
30
        """Return the number of lines contained in a Buffer."""
31 5
        return self.request('nvim_buf_line_count')
32
33 5
    def __getitem__(self, idx):
34
        """Get a buffer line or slice by integer index.
35
36
        Indexes may be negative to specify positions from the end of the
37
        buffer. For example, -1 is the last line, -2 is the line before that
38
        and so on.
39
40
        When retrieving slices, omiting indexes(eg: `buffer[:]`) will bring
41
        the whole buffer.
42
        """
43 5
        if not isinstance(idx, slice):
44 5
            i = adjust_index(idx)
45 5
            return self.request('nvim_buf_get_lines', i, i + 1, True)[0]
46 5
        start = adjust_index(idx.start, 0)
47 5
        end = adjust_index(idx.stop, -1)
48 5
        return self.request('nvim_buf_get_lines', start, end, False)
49
50 5
    def __setitem__(self, idx, item):
51
        """Replace a buffer line or slice by integer index.
52
53
        Like with `__getitem__`, indexes may be negative.
54
55
        When replacing slices, omiting indexes(eg: `buffer[:]`) will replace
56
        the whole buffer.
57
        """
58 5
        if not isinstance(idx, slice):
59 5
            i = adjust_index(idx)
60 5
            lines = [item] if item is not None else []
61 5
            return self.request('nvim_buf_set_lines', i, i + 1, True, lines)
62 5
        lines = item if item is not None else []
63 5
        start = adjust_index(idx.start, 0)
64 5
        end = adjust_index(idx.stop, -1)
65 5
        return self.request('nvim_buf_set_lines', start, end, False, lines)
66
67 5
    def __iter__(self):
68
        """Iterate lines of a buffer.
69
70
        This will retrieve all lines locally before iteration starts. This
71
        approach is used because for most cases, the gain is much greater by
72
        minimizing the number of API calls by transfering all data needed to
73
        work.
74
        """
75
        lines = self[:]
76
        for line in lines:
77
            yield line
78
79 5
    def __delitem__(self, idx):
80
        """Delete line or slice of lines from the buffer.
81
82
        This is the same as __setitem__(idx, [])
83
        """
84 5
        self.__setitem__(idx, None)
85
86 5
    def append(self, lines, index=-1):
87
        """Append a string or list of lines to the buffer."""
88 5
        if isinstance(lines, (basestring, bytes)):
89 5
            lines = [lines]
90 5
        return self.request('nvim_buf_set_lines', index, index, True, lines)
91
92 5
    def mark(self, name):
93
        """Return (row, col) tuple for a named mark."""
94 5
        return self.request('nvim_buf_get_mark', name)
95
96 5
    def range(self, start, end):
97
        """Return a `Range` object, which represents part of the Buffer."""
98
        return Range(self, start, end)
99
100 5
    def add_highlight(self, hl_group, line, col_start=0,
101
                      col_end=-1, src_id=-1, async_=None,
102
                      **kwargs):
103
        """Add a highlight to the buffer."""
104
        async_ = check_async(async_, kwargs, src_id != 0)
105
        return self.request('nvim_buf_add_highlight', src_id, hl_group,
106
                            line, col_start, col_end, async_=async_)
107
108 5
    def clear_highlight(self, src_id, line_start=0, line_end=-1, async_=None,
109
                        **kwargs):
110
        """Clear highlights from the buffer."""
111
        async_ = check_async(async_, kwargs, True)
112
        self.request('nvim_buf_clear_highlight', src_id,
113 5
                     line_start, line_end, async_=async_)
114
115
    def update_highlights(self, src_id, hls, clear_start=0, clear_end=-1, clear=False, async_=True):
116 5
        """Add or update highlights in batch to avoid redraws.
117
        A src_id must have been allocated prior to use of this function. Use for instance
118 5
        nvim.new_highlight_source() to get a src_id for your plugin.
119
120
        hls should be a list of highlight items. Each item should be a list or tuple on the form
121 5
        ("GroupName", linenr, col_start, col_end) or ("GroupName", linenr) to highlight an entire line.
122
123 5
        By default existing highlights is preserved. Specify a line range with clear_start and clear_end
124
        to replace highlights in this range. As a shorthand, use clear=True to clear the entire
125
        buffer before adding the new highlights.
126 5
        """
127
        if clear and clear_start is None:
128 5
            clear_start = 0
129
        lua = self._session._get_lua_private()
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _get_lua_private was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
130
        lua.update_highlights(self, src_id, hls, clear_start, clear_end, async_=async_)
131 5
132
    @property
133
    def name(self):
134 5
        """Get the buffer name."""
135 5
        return self.request('nvim_buf_get_name')
136
137
    @name.setter
138
    def name(self, value):
139
        """Set the buffer name. BufFilePre/BufFilePost are triggered."""
140 5
        return self.request('nvim_buf_set_name', value)
141
142
    @property
143 5
    def valid(self):
144
        """Return True if the buffer still exists."""
145
        return self.request('nvim_buf_is_valid')
146
147
    @property
148
    def number(self):
149
        """Get the buffer number."""
150
        return self.handle
151
152
153
class Range(object):
154 5
    def __init__(self, buffer, start, end):
155
        self._buffer = buffer
156
        self.start = start - 1
157
        self.end = end - 1
158
159
    def __len__(self):
160
        return self.end - self.start + 1
161
162
    def __getitem__(self, idx):
163
        if not isinstance(idx, slice):
164
            return self._buffer[self._normalize_index(idx)]
165
        start = self._normalize_index(idx.start)
166 5
        end = self._normalize_index(idx.stop)
167
        if start is None:
168
            start = self.start
169
        if end is None:
170 5
            end = self.end + 1
171
        return self._buffer[start:end]
172
173
    def __setitem__(self, idx, lines):
174
        if not isinstance(idx, slice):
175
            self._buffer[self._normalize_index(idx)] = lines
176 5
            return
177
        start = self._normalize_index(idx.start)
178
        end = self._normalize_index(idx.stop)
179
        if start is None:
180
            start = self.start
181
        if end is None:
182
            end = self.end
183
        self._buffer[start:end + 1] = lines
184
185
    def __iter__(self):
186
        for i in range(self.start, self.end + 1):
187
            yield self._buffer[i]
188
189
    def append(self, lines, i=None):
190
        i = self._normalize_index(i)
191
        if i is None:
192
            i = self.end + 1
193
        self._buffer.append(lines, i)
194
195
    def _normalize_index(self, index):
196
        if index is None:
197
            return None
198
        if index < 0:
199
            index = self.end
200
        else:
201
            index += self.start
202
            if index > self.end:
203
                index = self.end
204
        return index
205