Completed
Pull Request — master (#190)
by Björn
20:43 queued 19:30
created

adjust_index()   A

Complexity

Conditions 3

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
c 0
b 0
f 0
dl 0
loc 8
ccs 6
cts 6
cp 1
crap 3
rs 9.4285
1
"""API for working with a Nvim Buffer."""
2 6
from .common import Remote
3 6
from ..compat import IS_PYTHON3
4
5
6 6
__all__ = ('Buffer')
7
8
9 6
if IS_PYTHON3:
10 3
    basestring = str
11
12
13 6
def adjust_index(idx, default=None):
14
    """Convert from python indexing convention to nvim indexing convention."""
15 6
    if idx is None:
16 6
        return default
17 6
    elif idx < 0:
18 6
        return idx - 1
19
    else:
20 6
        return idx
21
22
23 6
class Buffer(Remote):
24
25
    """A remote Nvim buffer."""
26
27 6
    _api_prefix = "buffer_"
28
29 6
    def __len__(self):
30
        """Return the number of lines contained in a Buffer."""
31 6
        return self.request('buffer_line_count')
32
33 6
    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 6
        if not isinstance(idx, slice):
44 6
            i = adjust_index(idx)
45 6
            return self.request('buffer_get_lines', i, i + 1, True)[0]
46 6
        start = adjust_index(idx.start, 0)
47 6
        end = adjust_index(idx.stop, -1)
48 6
        return self.request('buffer_get_lines', start, end, False)
49
50 6
    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 6
        if not isinstance(idx, slice):
59 6
            i = adjust_index(idx)
60 6
            lines = [item] if item is not None else []
61 6
            return self.request('buffer_set_lines', i, i + 1, True, lines)
62 6
        lines = item if item is not None else []
63 6
        start = adjust_index(idx.start, 0)
64 6
        end = adjust_index(idx.stop, -1)
65 6
        return self.request('buffer_set_lines', start, end, False, lines)
66
67 6
    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 6
    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 6
        self.__setitem__(idx, None)
85
86 6
    def append(self, lines, index=-1):
87
        """Append a string or list of lines to the buffer."""
88 6
        if isinstance(lines, (basestring, bytes)):
89 6
            lines = [lines]
90 6
        return self.request('buffer_set_lines', index, index, True, lines)
91
92 6
    def mark(self, name):
93
        """Return (row, col) tuple for a named mark."""
94 6
        return self.request('buffer_get_mark', name)
95
96 6
    def range(self, start, end):
97
        """Return a `Range` object, which represents part of the Buffer."""
98
        return Range(self, start, end)
99
100 6
    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('buffer_add_highlight', src_id, hl_group,
106
                            line, col_start, col_end, async=async)
107
108 6
    def clear_highlight(self, src_id, line_start=0, line_end=-1, async=True):
109
        """Clear highlights from the buffer."""
110
        self.request('buffer_clear_highlight', src_id,
111
                     line_start, line_end, async=async)
112
113 6
    @property
114
    def name(self):
115
        """Get the buffer name."""
116 6
        return self.request('buffer_get_name')
117
118 6
    @name.setter
119
    def name(self, value):
120
        """Set the buffer name. BufFilePre/BufFilePost are triggered."""
121 6
        return self.request('buffer_set_name', value)
122
123 6
    @property
124
    def valid(self):
125
        """Return True if the buffer still exists."""
126 6
        return self.request('buffer_is_valid')
127
128 6
    @property
129
    def number(self):
130
        """Get the buffer number."""
131 6
        return self.request('buffer_get_number')
132
133
134 6
class Range(object):
135 6
    def __init__(self, buffer, start, end):
136
        self._buffer = buffer
137
        self.start = start - 1
138
        self.end = end - 1
139
140 6
    def __len__(self):
141
        return self.end - self.start + 1
142
143 6
    def __getitem__(self, idx):
144
        if not isinstance(idx, slice):
145
            return self._buffer[self._normalize_index(idx)]
146
        start = self._normalize_index(idx.start)
147
        end = self._normalize_index(idx.stop)
148
        if start is None:
149
            start = self.start
150
        if end is None:
151
            end = self.end + 1
152
        return self._buffer[start:end]
153
154 6
    def __setitem__(self, idx, lines):
155
        if not isinstance(idx, slice):
156
            self._buffer[self._normalize_index(idx)] = lines
157
            return
158
        start = self._normalize_index(idx.start)
159
        end = self._normalize_index(idx.stop)
160
        if start is None:
161
            start = self.start
162
        if end is None:
163
            end = self.end + 1
164
        self._buffer[start:end] = lines
165
166 6
    def __iter__(self):
167
        for i in range(self.start, self.end + 1):
168
            yield self._buffer[i]
169
170 6
    def append(self, lines, i=None):
171
        i = self._normalize_index(i)
172
        if i is None:
173
            i = self.end + 1
174
        self._buffer.append(lines, i)
175
176 6
    def _normalize_index(self, index):
177
        if index is None:
178
            return None
179
        if index < 0:
180
            index = self.end
181
        else:
182
            index += self.start
183
            if index > self.end:
184
                index = self.end
185
        return index
186