dte test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 50.0% high: ≥ 85.0%
Coverage Exec / Excl / Total
Lines: 84.6% 165 / 0 / 195
Functions: 100.0% 13 / 0 / 13
Branches: 74.0% 71 / 10 / 106

src/indent.c
Line Branch Exec Source
1 #include <stdlib.h>
2 #include <string.h>
3 #include <sys/types.h>
4 #include "indent.h"
5 #include "block-iter.h"
6 #include "buffer.h"
7 #include "change.h"
8 #include "move.h"
9 #include "regexp.h"
10 #include "selection.h"
11 #include "util/xmalloc.h"
12
13 51 String make_indent(const LocalOptions *options, size_t width)
14 {
15
2/2
✓ Branch 2 → 3 taken 20 times.
✓ Branch 2 → 4 taken 31 times.
51 if (width == 0) {
16 20 return string_new(0);
17 }
18
19 31 bool use_spaces = use_spaces_for_indent(options);
20 31 size_t tw = options->tab_width;
21
2/2
✓ Branch 4 → 5 taken 8 times.
✓ Branch 4 → 7 taken 23 times.
31 size_t ntabs = use_spaces ? 0 : indent_level(width, tw);
22 8 size_t nspaces = use_spaces ? width : indent_remainder(width, tw);
23 31 size_t nbytes = ntabs + nspaces;
24 31 BUG_ON(nbytes == 0);
25
26 31 String str = string_new(nbytes + 1); // +1 for efficiency in several callers
27 31 memset(str.buffer, '\t', ntabs);
28 31 memset(str.buffer + ntabs, ' ', nspaces);
29 31 str.len = nbytes;
30 31 return str;
31 }
32
33 // Return true if the contents of `line` triggers an additional level
34 // of auto-indent on the next line
35 37 static bool line_contents_increases_indent (
36 const LocalOptions *options,
37 StringView line
38 ) {
39
2/2
✓ Branch 2 → 3 taken 36 times.
✓ Branch 2 → 16 taken 1 time.
37 if (!line.length) {
40 return false;
41 }
42
43 36 static const regex_t *re1, *re2;
44
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 7 taken 35 times.
36 if (!re1) {
45 // TODO: Make these patterns configurable via a local option
46 1 re1 = regexp_compile_or_fatal_error("\\{[\t ]*(//.*|/\\*.*\\*/[\t ]*)?$");
47 1 re2 = regexp_compile_or_fatal_error("\\}[\t ]*(//.*|/\\*.*\\*/[\t ]*)?$");
48 }
49
50
2/2
✓ Branch 7 → 8 taken 13 times.
✓ Branch 7 → 12 taken 23 times.
36 if (options->brace_indent) {
51
2/2
✓ Branch 9 → 10 taken 12 times.
✓ Branch 9 → 16 taken 1 time.
13 if (regexp_exec(re1, line.data, line.length, 0, NULL, 0)) {
52 return true;
53 }
54
1/2
✓ Branch 11 → 12 taken 12 times.
✗ Branch 11 → 16 not taken.
12 if (regexp_exec(re2, line.data, line.length, 0, NULL, 0)) {
55 return false;
56 }
57 }
58
59 35 const InternedRegexp *ir = options->indent_regex;
60
2/2
✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 16 taken 33 times.
35 if (!ir) {
61 return false;
62 }
63
64 2 BUG_ON(ir->str[0] == '\0');
65 2 return regexp_exec(&ir->re, line.data, line.length, 0, NULL, 0);
66 }
67
68 37 String get_indent_for_next_line(const LocalOptions *options, StringView line)
69 {
70 37 size_t curr_width = get_indent_width(line, options->tab_width);
71 37 size_t next_width = next_indent_width(curr_width, options->indent_width);
72 37 bool increase = line_contents_increases_indent(options, line);
73
2/2
✓ Branch 5 → 6 taken 35 times.
✓ Branch 5 → 7 taken 2 times.
72 return make_indent(options, increase ? next_width : curr_width);
74 }
75
76 64 IndentInfo get_indent_info(const LocalOptions *options, StringView line)
77 {
78 64 const char *buf = line.data;
79 64 const size_t len = line.length;
80 64 const size_t tw = options->tab_width;
81 64 const size_t iw = options->indent_width;
82 64 const bool space_indent = use_spaces_for_indent(options);
83 64 IndentInfo info = {.sane = true};
84 64 size_t spaces = 0;
85 64 size_t tabs = 0;
86 64 size_t pos = 0;
87
88
2/2
✓ Branch 16 → 3 taken 631 times.
✓ Branch 16 → 17 taken 5 times.
636 for (; pos < len; pos++) {
89
2/2
✓ Branch 3 → 4 taken 564 times.
✓ Branch 3 → 5 taken 67 times.
631 if (buf[pos] == ' ') {
90 564 info.width++;
91 564 spaces++;
92
2/2
✓ Branch 5 → 6 taken 8 times.
✓ Branch 5 → 17 taken 59 times.
67 } else if (buf[pos] == '\t') {
93 8 info.width = next_indent_width(info.width, tw);
94 8 tabs++;
95 } else {
96 break;
97 }
98
4/4
✓ Branch 9 → 10 taken 146 times.
✓ Branch 9 → 15 taken 426 times.
✓ Branch 10 → 11 taken 145 times.
✓ Branch 10 → 15 taken 1 time.
572 if (indent_remainder(info.width, iw) == 0 && info.sane) {
99
2/2
✓ Branch 11 → 12 taken 137 times.
✓ Branch 11 → 13 taken 8 times.
145 info.sane = space_indent ? !tabs : !spaces;
100 }
101 }
102
103 64 info.level = indent_level(info.width, iw);
104 64 info.wsonly = (pos == len);
105 64 info.bytes = spaces + tabs;
106 64 return info;
107 }
108
109 49 size_t get_indent_width(StringView line, unsigned int tab_width)
110 {
111 49 const char *buf = line.data;
112 49 size_t width = 0;
113
2/2
✓ Branch 8 → 3 taken 139 times.
✓ Branch 8 → 9 taken 4 times.
143 for (size_t i = 0, n = line.length; i < n; i++) {
114
2/2
✓ Branch 3 → 4 taken 88 times.
✓ Branch 3 → 5 taken 51 times.
139 if (buf[i] == ' ') {
115 88 width++;
116
2/2
✓ Branch 5 → 6 taken 6 times.
✓ Branch 5 → 9 taken 45 times.
51 } else if (buf[i] == '\t') {
117 6 width = next_indent_width(width, tab_width);
118 } else {
119 break;
120 }
121 }
122 49 return width;
123 }
124
125 22 static ssize_t get_current_indent_bytes (
126 const char *buf,
127 size_t cursor_offset,
128 unsigned int iw,
129 unsigned int tw
130 ) {
131 22 size_t bytes = 0;
132 22 size_t width = 0;
133
134
2/2
✓ Branch 11 → 3 taken 20 times.
✓ Branch 11 → 12 taken 2 times.
22 for (size_t i = 0; i < cursor_offset; i++) {
135
1/2
✓ Branch 4 → 5 taken 20 times.
✗ Branch 4 → 6 not taken.
20 if (indent_remainder(width, iw) == 0) {
136 20 bytes = 0;
137 20 width = 0;
138 }
139
1/3
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 9 not taken.
✓ Branch 6 → 15 taken 20 times.
20 switch (buf[i]) {
140 case '\t':
141 width = next_indent_width(width, tw);
142 break;
143 case ' ':
144 width++;
145 break;
146 default:
147 // Cursor not at indentation
148 return -1;
149 }
150 bytes++;
151 }
152
153
1/2
✓ Branch 13 → 14 taken 2 times.
✗ Branch 13 → 15 not taken.
2 if (indent_remainder(width, iw)) {
154 // Cursor at middle of indentation level
155 return -1;
156 }
157
158 2 return (ssize_t)bytes;
159 }
160
161 3 size_t get_indent_level_bytes_left(const LocalOptions *options, const BlockIter *cursor)
162 {
163 3 BlockIter bol = *cursor;
164 3 size_t cursor_offset = block_iter_bol(&bol);
165
1/2
✓ Branch 3 → 4 taken 3 times.
✗ Branch 3 → 7 not taken.
3 if (cursor_offset == 0) {
166 return 0; // cursor at BOL
167 }
168
169 3 StringView line = block_iter_get_line(&bol);
170 3 unsigned int iw = options->indent_width;
171 3 unsigned int tw = options->tab_width;
172 3 ssize_t ibytes = get_current_indent_bytes(line.data, cursor_offset, iw, tw);
173 3 return MAX(ibytes, 0);
174 }
175
176 19 size_t get_indent_level_bytes_right(const LocalOptions *options, const BlockIter *cursor)
177 {
178 19 unsigned int iw = options->indent_width;
179 19 unsigned int tw = options->tab_width;
180 19 CurrentLineRef lr = get_current_line_and_offset(*cursor);
181 19 ssize_t ibytes = get_current_indent_bytes(lr.line.data, lr.cursor_offset, iw, tw);
182
2/2
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 15 taken 17 times.
19 if (ibytes < 0) {
183 return 0;
184 }
185
186 2 size_t width = 0;
187
1/2
✓ Branch 14 → 6 taken 3 times.
✗ Branch 14 → 15 not taken.
3 for (size_t i = lr.cursor_offset, n = lr.line.length; i < n; i++) {
188
2/3
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 9 taken 1 time.
✓ Branch 6 → 15 taken 2 times.
3 switch (lr.line.data[i]) {
189 case '\t':
190 width = next_indent_width(width, tw);
191 break;
192 1 case ' ':
193 1 width++;
194 1 break;
195 default:
196 // No full indentation level at cursor position
197 return 0;
198 }
199
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 1 time.
1 if (indent_remainder(width, iw) == 0) {
200 return i - lr.cursor_offset + 1;
201 }
202 }
203
204 return 0;
205 }
206
207 10 static char *alloc_indent(const LocalOptions *options, size_t count, size_t *sizep)
208 {
209 10 bool use_spaces = use_spaces_for_indent(options);
210
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 9 times.
10 size_t size = use_spaces ? count * options->indent_width : count;
211 10 *sizep = size;
212 10 return memset(xmalloc(size), use_spaces ? ' ' : '\t', size);
213 }
214
215 10 static void increase_indent(View *view, size_t nr_lines, size_t count)
216 {
217 10 BUG_ON(!block_iter_is_bol(&view->cursor));
218 10 const LocalOptions *options = &view->buffer->options;
219 10 size_t indent_size;
220 10 char *indent = alloc_indent(options, count, &indent_size);
221
222 30 for (size_t i = 0; true; ) {
223 30 StringView line = block_iter_get_line(&view->cursor);
224 30 IndentInfo info = get_indent_info(options, line);
225
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 10 taken 30 times.
30 if (info.wsonly) {
226 if (info.bytes) {
227 // Remove indentation
228 buffer_delete_bytes(view, info.bytes);
229 }
230
1/2
✓ Branch 10 → 11 taken 30 times.
✗ Branch 10 → 12 not taken.
30 } else if (info.sane) {
231 // Insert whitespace
232 30 buffer_insert_bytes(view, indent, indent_size);
233 } else {
234 // Replace whole indentation with sane one
235 size_t size;
236 char *buf = alloc_indent(options, info.level + count, &size);
237 buffer_replace_bytes(view, info.bytes, buf, size);
238 free(buf);
239 }
240
2/2
✓ Branch 15 → 16 taken 20 times.
✓ Branch 15 → 18 taken 10 times.
30 if (++i == nr_lines) {
241 break;
242 }
243 20 block_iter_eat_line(&view->cursor);
244 }
245
246 10 free(indent);
247 10 }
248
249 9 static void decrease_indent(View *view, size_t nr_lines, size_t count)
250 {
251 9 BUG_ON(!block_iter_is_bol(&view->cursor));
252 9 const LocalOptions *options = &view->buffer->options;
253 9 const size_t indent_width = options->indent_width;
254 9 const bool space_indent = use_spaces_for_indent(options);
255
256 25 for (size_t i = 0; true; ) {
257 25 StringView line = block_iter_get_line(&view->cursor);
258 25 IndentInfo info = get_indent_info(options, line);
259
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 10 taken 25 times.
25 if (info.wsonly) {
260 if (info.bytes) {
261 // Remove indentation
262 buffer_delete_bytes(view, info.bytes);
263 }
264
3/4
✓ Branch 10 → 11 taken 23 times.
✓ Branch 10 → 16 taken 2 times.
✓ Branch 11 → 12 taken 23 times.
✗ Branch 11 → 16 not taken.
48 } else if (info.level && info.sane) {
265 23 size_t n = MIN(count, info.level);
266
1/2
✓ Branch 12 → 13 taken 23 times.
✗ Branch 12 → 14 not taken.
23 if (space_indent) {
267 23 n *= indent_width;
268 }
269 23 buffer_delete_bytes(view, n);
270
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 22 taken 2 times.
2 } else if (info.bytes) {
271 // Replace whole indentation with sane one
272 if (info.level > count) {
273 size_t size;
274 char *buf = alloc_indent(options, info.level - count, &size);
275 buffer_replace_bytes(view, info.bytes, buf, size);
276 free(buf);
277 } else {
278 buffer_delete_bytes(view, info.bytes);
279 }
280 }
281
2/2
✓ Branch 22 → 23 taken 16 times.
✓ Branch 22 → 25 taken 9 times.
25 if (++i == nr_lines) {
282 break;
283 }
284 16 block_iter_eat_line(&view->cursor);
285 }
286 9 }
287
288 19 static void do_indent_lines(View *view, int count, size_t nr_lines)
289 {
290 19 begin_change_chain();
291 19 block_iter_bol(&view->cursor);
292
2/2
✓ Branch 4 → 5 taken 10 times.
✓ Branch 4 → 6 taken 9 times.
19 if (count > 0) {
293 10 increase_indent(view, nr_lines, count);
294 } else {
295 9 decrease_indent(view, nr_lines, -count);
296 }
297 19 end_change_chain(view);
298 19 }
299
300 20 void indent_lines(View *view, int count)
301 {
302
2/2
✓ Branch 2 → 3 taken 19 times.
✓ Branch 2 → 26 taken 1 time.
20 if (unlikely(count == 0)) {
303 1 return;
304 }
305
306 19 int width = view->buffer->options.indent_width;
307 19 BUG_ON(width < 1 || width > INDENT_WIDTH_MAX);
308 19 long x = view_get_preferred_x(view) + (count * width);
309 19 x = MAX(x, 0);
310
311
2/2
✓ Branch 6 → 7 taken 10 times.
✓ Branch 6 → 9 taken 9 times.
19 if (view->selection == SELECT_NONE) {
312 10 do_indent_lines(view, count, 1);
313 10 goto out;
314 }
315
316 9 view->selection = SELECT_LINES;
317 9 SelectionInfo info = init_selection(view);
318 9 view->cursor = info.si;
319 9 size_t nr_lines = get_nr_selected_lines(&info);
320
1/2
✓ Branch 11 → 12 taken 9 times.
✗ Branch 11 → 26 not taken.
9 if (unlikely(nr_lines == 0)) {
321 return;
322 }
323
324 9 do_indent_lines(view, count, nr_lines);
325
1/2
✓ Branch 13 → 14 taken 9 times.
✗ Branch 13 → 19 not taken.
9 if (info.swapped) {
326 // Cursor should be at beginning of selection
327 9 block_iter_bol(&view->cursor);
328 9 view->sel_so = block_iter_get_offset(&view->cursor);
329
2/2
✓ Branch 18 → 17 taken 36 times.
✓ Branch 18 → 24 taken 9 times.
45 while (--nr_lines) {
330 36 block_iter_prev_line(&view->cursor);
331 }
332 } else {
333 BlockIter save = view->cursor;
334 while (--nr_lines) {
335 block_iter_prev_line(&view->cursor);
336 }
337 view->sel_so = block_iter_get_offset(&view->cursor);
338 view->cursor = save;
339 }
340
341 19 out:
342 19 move_to_preferred_x(view, x);
343 }
344