dte test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 50.0% high: ≥ 85.0%
Coverage Exec / Excl / Total
Lines: 85.9% 171 / 0 / 199
Functions: 100.0% 13 / 0 / 13
Branches: 72.6% 77 / 18 / 124

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