Completed
Pull Request — master (#269)
by
unknown
20:27
created

Selection.__len__()   A

Complexity

Conditions 1

Size

Total Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
dl 0
loc 2
ccs 0
cts 0
cp 0
crap 2
rs 10
1
"""API for working with a Nvim Buffer."""
2 5
from .common import Remote
3 5
from ..compat import IS_PYTHON3
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('buffer_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('buffer_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
        """Add a highlight to the buffer."""
103
        if async is None:
104
            async = (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=True):
109
        """Clear highlights from the buffer."""
110
        self.request('nvim_buf_clear_highlight', src_id,
111
                     line_start, line_end, async=async)
112
113 5
    @property
114
    def name(self):
115
        """Get the buffer name."""
116 5
        return self.request('nvim_buf_get_name')
117
118 5
    @name.setter
119
    def name(self, value):
120
        """Set the buffer name. BufFilePre/BufFilePost are triggered."""
121 5
        return self.request('nvim_buf_set_name', value)
122
123 5
    @property
124
    def valid(self):
125
        """Return True if the buffer still exists."""
126 5
        return self.request('nvim_buf_is_valid')
127
128 5
    @property
129
    def number(self):
130
        """Get the buffer number."""
131 5
        return self.handle
132
133
    @property
134 5
    def visual_selection(self):
135 5
        """Get the current visual selection"""
136
        startmark = self.mark('<')
137
        endmark = self.mark('>')
138
        return Selection(self, startmark, endmark)
139
140 5
141
class Range(object):
142
    def __init__(self, buffer, start, end):
143 5
        self._buffer = buffer
144
        self.start = start - 1
145
        self.end = end - 1
146
147
    def __len__(self):
148
        return self.end - self.start + 1
149
150
    def __getitem__(self, idx):
151
        if not isinstance(idx, slice):
152
            return self._buffer[self._normalize_index(idx)]
153
        start = self._normalize_index(idx.start)
154 5
        end = self._normalize_index(idx.stop)
155
        if start is None:
156
            start = self.start
157
        if end is None:
158
            end = self.end + 1
159
        return self._buffer[start:end]
160
161
    def __setitem__(self, idx, lines):
162
        if not isinstance(idx, slice):
163
            self._buffer[self._normalize_index(idx)] = lines
164
            return
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
        self._buffer[start:end] = lines
172
173
    def __iter__(self):
174
        for i in range(self.start, self.end + 1):
175
            yield self._buffer[i]
176 5
177
    def append(self, lines, i=None):
178
        i = self._normalize_index(i)
179
        if i is None:
180
            i = self.end + 1
181
        self._buffer.append(lines, i)
182
183
    def _normalize_index(self, index):
184
        if index is None:
185
            return None
186
        if index < 0:
187
            index = self.end
188
        else:
189
            index += self.start
190
            if index > self.end:
191
                index = self.end
192
        return index
193
194
195
class Selection(object):
196
    def __init__(self, buffer, startmark, endmark):
197
        self._range = buffer.range(startmark[0], endmark[0] + 1)
198
        self.slice = slice(startmark[1], endmark[1] + 1)
199
200
    def __len__(self):
201
        return len(self._range)
202
203
    def __getitem__(self, idx):
204
        if not isinstance(idx, slice):
205
            return self._range[idx]
206
        start = idx.start
207
        end = idx.stop
208
        if start is None:
209
            start = self._range.start
210
        if end is None:
211
            end = self._range.stop
212
        return [l[self.slice] for l in self._range[start:end]]
213
214
    def __setitem__(self, idx, lineparts):
215
        if not isinstance(idx, slice):
216
            new_line = self._assemble_line(self._range[idx], lineparts)
217
            self._range[idx] = new_line
218
            return
219
        start = idx.start
220
        end = idx.stop
221
        if start is None:
222
            start = self._range.start
223
        if end is None:
224
            end = self._range.stop
225
        assert(end - start == len(lineparts))
0 ignored issues
show
Unused Code Coding Style introduced by
There is an unnecessary parenthesis after assert.
Loading history...
226
        lines = []
227
        for i in range(start, end):
228
            ni = i = start  # normalized index
229
            lines.append(self._assemble_line(self._range[i], lineparts[ni]))
230
        self._range[start:end] = lines
231
232
    def __iter__(self):
233
        for i in range(self._range.start, self._range.end + 1):
234
            yield self._buffer[i][self.slice]
0 ignored issues
show
Bug introduced by
The Instance of Selection does not seem to have a member named _buffer.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
235
236
    def _assemble_line(self, orig_line, replacement):
237
            orig_prefix = orig_line[:self.slice.start]
238
            orig_suffix = orig_line[self.slice.stop:]
239
            new_line = orig_prefix + replacement + orig_suffix
240
            return new_line
241