dte test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 50.0% high: ≥ 85.0%
Coverage Exec / Excl / Total
Lines: 50.0% 122 / 2 / 246
Functions: 66.7% 10 / 0 / 15
Branches: 40.6% 52 / 6 / 134

src/syntax/highlight.c
Line Branch Exec Source
1 #include <stdint.h>
2 #include <string.h>
3 #include <sys/types.h>
4 #include "highlight.h"
5 #include "syntax/merge.h"
6 #include "util/arith.h"
7 #include "util/bit.h"
8 #include "util/intern.h"
9 #include "util/xmalloc.h"
10 #include "util/xstring.h"
11
12 1 static bool state_is_valid(const State *st)
13 {
14 1 return ((uintptr_t)st & 1) == 0;
15 }
16
17 1 static void mark_state_invalid(void **ptrs, size_t idx)
18 {
19 1 const State *st = ptrs[idx];
20 1 ptrs[idx] = (State*)((uintptr_t)st | 1);
21 1 }
22
23 1 static bool states_equal(void **ptrs, size_t idx, const State *b)
24 {
25 1 const State *a = (State*)((uintptr_t)ptrs[idx] & ~(uintptr_t)1);
26 1 return a == b;
27 }
28
29 1 static bool bufis(const ConditionData *u, const char *buf, size_t len)
30 {
31 1 size_t ulen = u->str.len;
32
2/4
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 6 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1 time.
1 return (ulen == len) && mem_equal(u->str.buf, buf, len);
33 }
34
35 static bool bufis_icase(const ConditionData *u, const char *buf, size_t len)
36 {
37 size_t ulen = u->str.len;
38 return (ulen == len) && mem_equal_icase(u->str.buf, buf, len);
39 }
40
41 static State *handle_heredoc (
42 Syntax *syn,
43 State *state,
44 const StyleMap *sm,
45 const char *delim,
46 size_t len
47 ) {
48 delim = mem_intern(delim, len);
49 for (size_t i = 0, n = state->heredoc.states.count; i < n; i++) {
50 HeredocState *s = state->heredoc.states.ptrs[i];
51 if (interned_strings_equal(s->delim, delim)) {
52 BUG_ON(s->len != len);
53 return s->state;
54 }
55 }
56
57 SyntaxMerge m = {
58 .subsyn = state->heredoc.subsyntax,
59 .return_state = state->default_action.destination,
60 .delim = delim,
61 .delim_len = len
62 };
63
64 HeredocState *s = xmalloc(sizeof(*s));
65 *s = (HeredocState) {
66 .state = merge_syntax(syn, &m, sm),
67 .delim = delim,
68 .len = len,
69 };
70
71 ptr_array_append(&state->heredoc.states, s);
72 return s->state;
73 }
74
75 // Line should be terminated with \n unless it's the last line
76 8 static const TermStyle **highlight_line (
77 Syntax *syn,
78 State *state,
79 const StyleMap *sm,
80 StringView line_sv,
81 State **ret
82 ) {
83 8 static const TermStyle **styles; // NOLINT(*-avoid-non-const-global-variables)
84 8 static size_t alloc; // NOLINT(*-avoid-non-const-global-variables)
85 8 const char *const line = line_sv.data;
86 8 const size_t len = line_sv.length;
87 8 size_t i = 0;
88 8 ssize_t sidx = -1;
89
90
2/2
✓ Branch 2 → 3 taken 7 times.
✓ Branch 2 → 4 taken 1 time.
8 if (len > alloc) {
91 1 alloc = next_multiple(len, 128);
92 1 styles = xrenew(styles, alloc);
93 }
94
95 8 top:
96
2/2
✓ Branch 8 → 9 taken 8 times.
✓ Branch 8 → 12 taken 242 times.
250 if (i >= len) {
97 8 BUG_ON(i > len);
98 8 *ret = state;
99 8 return styles;
100 }
101
102
2/2
✓ Branch 65 → 13 taken 955 times.
✓ Branch 65 → 66 taken 104 times.
1059 for (size_t ci = 0, n = state->conds.count; ci < n; ci++) {
103 955 const Condition *cond = state->conds.ptrs[ci];
104 955 const ConditionData *u = &cond->u;
105 955 const ConditionType condtype = cond->type;
106 955 const TermStyle *style = cond->a.emit_style;
107 955 State *dest = cond->a.destination;
108
6/13
✓ Branch 13 → 14 taken 225 times.
✓ Branch 13 → 18 taken 1 time.
✗ Branch 13 → 23 not taken.
✓ Branch 13 → 28 taken 92 times.
✓ Branch 13 → 30 taken 448 times.
✓ Branch 13 → 32 taken 78 times.
✗ Branch 13 → 39 not taken.
✗ Branch 13 → 40 not taken.
✗ Branch 13 → 42 not taken.
✗ Branch 13 → 47 not taken.
✓ Branch 13 → 52 taken 111 times.
✗ Branch 13 → 56 not taken.
✗ Branch 13 → 62 not taken.
955 switch (condtype) {
109 225 case COND_CHAR_BUFFER:
110
2/2
✓ Branch 14 → 15 taken 82 times.
✓ Branch 14 → 64 taken 143 times.
225 if (!bitset_contains(u->bitset, line[i])) {
111 break;
112 }
113
2/2
✓ Branch 15 → 16 taken 14 times.
✓ Branch 15 → 17 taken 68 times.
82 if (sidx < 0) {
114 14 sidx = i;
115 }
116 82 styles[i++] = style;
117 82 state = dest;
118 82 goto top;
119 1 case COND_BUFIS:
120
2/4
✓ Branch 18 → 19 taken 1 time.
✗ Branch 18 → 64 not taken.
✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 64 not taken.
1 if (sidx < 0 || !bufis(u, line + sidx, i - sidx)) {
121 break;
122 }
123 1 set_style_range(styles, style, sidx, i);
124 1 sidx = -1;
125 1 state = dest;
126 1 goto top;
127 case COND_BUFIS_ICASE:
128 if (sidx < 0 || !bufis_icase(u, line + sidx, i - sidx)) {
129 break;
130 }
131 set_style_range(styles, style, sidx, i);
132 sidx = -1;
133 state = dest;
134 goto top;
135 92 case COND_CHAR:
136
2/2
✓ Branch 28 → 29 taken 19 times.
✓ Branch 28 → 64 taken 73 times.
92 if (!bitset_contains(u->bitset, line[i])) {
137 break;
138 }
139 19 styles[i++] = style;
140 19 sidx = -1;
141 19 state = dest;
142 19 goto top;
143 448 case COND_CHAR1:
144
2/2
✓ Branch 30 → 31 taken 28 times.
✓ Branch 30 → 64 taken 420 times.
448 if (u->ch != line[i]) {
145 break;
146 }
147 28 styles[i++] = style;
148 28 sidx = -1;
149 28 state = dest;
150 28 goto top;
151 78 case COND_INLIST:
152 case COND_INLIST_BUFFER:
153
4/4
✓ Branch 32 → 33 taken 75 times.
✓ Branch 32 → 64 taken 3 times.
✓ Branch 34 → 35 taken 8 times.
✓ Branch 34 → 64 taken 67 times.
78 if (sidx < 0 || !hashset_get(&u->str_list->strings, line + sidx, i - sidx)) {
154 break;
155 }
156 8 set_style_range(styles, style, sidx, i);
157
1/2
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 8 times.
8 sidx = (condtype == COND_INLIST) ? -1 : sidx;
158 8 state = dest;
159 8 goto top;
160 case COND_RECOLOR:
161 set_style_range(styles, style, saturating_subtract(i, u->recolor_len), i);
162 break;
163 case COND_RECOLOR_BUFFER:
164 if (sidx >= 0) {
165 set_style_range(styles, style, sidx, i);
166 sidx = -1;
167 }
168 break;
169 case COND_STR: {
170 size_t slen = u->str.len;
171 size_t end = i + slen;
172 if (len < end || !mem_equal(u->str.buf, line + i, slen)) {
173 break;
174 }
175 i += set_style_range(styles, style, i, end);
176 sidx = -1;
177 state = dest;
178 goto top;
179 }
180 case COND_STR_ICASE: {
181 size_t slen = u->str.len;
182 size_t end = i + slen;
183 if (len < end || !mem_equal_icase(u->str.buf, line + i, slen)) {
184 break;
185 }
186 i += set_style_range(styles, style, i, end);
187 sidx = -1;
188 state = dest;
189 goto top;
190 }
191 111 case COND_STR2:
192 // Optimized COND_STR (length 2, case sensitive)
193
2/4
✓ Branch 52 → 53 taken 111 times.
✗ Branch 52 → 64 not taken.
✗ Branch 54 → 55 not taken.
✓ Branch 54 → 64 taken 111 times.
111 if (len < i + 2 || !mem_equal(u->str.buf, line + i, 2)) {
194 break;
195 }
196 styles[i++] = style;
197 styles[i++] = style;
198 sidx = -1;
199 state = dest;
200 goto top;
201 case COND_HEREDOCEND: {
202 StringView hde = u->heredocend;
203 size_t end = i + hde.length;
204 if (len >= end && mem_equal(hde.data, line + i, hde.length)) {
205 i += set_style_range(styles, style, i, end);
206 sidx = -1;
207 state = dest;
208 goto top;
209 }
210 } break;
211 default:
212 BUG("unhandled condition type");
213 }
214 }
215
216
3/5
✓ Branch 66 → 67 taken 18 times.
✓ Branch 66 → 68 taken 82 times.
✓ Branch 66 → 69 taken 4 times.
✗ Branch 66 → 70 not taken.
✗ Branch 66 → 73 not taken.
104 switch (state->type) {
217 82 case STATE_EAT:
218 82 styles[i++] = state->default_action.emit_style;
219 // Fallthrough
220 case STATE_NOEAT:
221 sidx = -1;
222 // Fallthrough
223 104 case STATE_NOEAT_BUFFER:
224 104 state = state->default_action.destination;
225 104 break;
226 case STATE_HEREDOCBEGIN:
227 if (sidx < 0) {
228 sidx = i;
229 }
230 state = handle_heredoc(syn, state, sm, line + sidx, i - sidx);
231 break;
232 case STATE_INVALID:
233 default:
234 BUG("unhandled default action type");
235 }
236
237 104 goto top;
238 }
239
240 1 static void resize_line_states(PointerArray *s, size_t count)
241 {
242
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 1 time.
1 if (s->alloc < count) {
243 s->alloc = next_multiple(count, 64);
244 s->ptrs = xrenew(s->ptrs, s->alloc);
245 }
246 1 }
247
248 static void move_line_states (
249 PointerArray *s,
250 size_t to,
251 size_t from,
252 size_t count
253 ) {
254 memmove(s->ptrs + to, s->ptrs + from, count * sizeof(*s->ptrs));
255 }
256
257 1 static void block_iter_move_down(BlockIter *bi, size_t count)
258 {
259
1/2
✗ Branch 4 → 3 not taken.
✓ Branch 4 → 5 taken 1 time.
1 while (count--) {
260 block_iter_eat_line(bi);
261 }
262 1 }
263
264 static ssize_t fill_hole (
265 Syntax *syn,
266 PointerArray *line_start_states,
267 const StyleMap *sm,
268 BlockIter *bi,
269 ssize_t sidx,
270 ssize_t eidx
271 ) {
272 void **ptrs = line_start_states->ptrs;
273 ssize_t idx = sidx;
274
275 while (idx < eidx) {
276 State *st;
277 StringView line = block_iter_get_line_with_nl(bi);
278 block_iter_eat_line(bi);
279 highlight_line(syn, ptrs[idx++], sm, line, &st);
280
281 if (ptrs[idx] == st) {
282 // Was not invalidated and didn't change
283 break;
284 }
285
286 if (states_equal(ptrs, idx, st)) {
287 // Was invalidated and didn't change
288 ptrs[idx] = st;
289 } else {
290 // Invalidated or not but changed anyway
291 ptrs[idx] = st;
292 if (idx == eidx) {
293 mark_state_invalid(ptrs, idx + 1);
294 }
295 }
296 }
297 return idx - sidx;
298 }
299
300 1 void hl_fill_start_states (
301 Syntax *syn,
302 PointerArray *line_start_states,
303 const StyleMap *sm,
304 BlockIter *bi,
305 size_t line_nr
306 ) {
307
1/2
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 22 not taken.
1 if (!syn) {
308 return;
309 }
310
311 1 PointerArray *s = line_start_states;
312 1 ssize_t current_line = 0;
313 1 ssize_t idx = 0;
314
315 // NOTE: "+ 2" so that you don't have to worry about overflow in fill_hole()
316 1 resize_line_states(s, line_nr + 2);
317 1 State **states = (State **)s->ptrs;
318
319 // Update invalid
320 1 ssize_t last = line_nr;
321
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 7 not taken.
1 if (last >= s->count) {
322 1 last = s->count - 1;
323 }
324 while (1) {
325
3/4
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 11 taken 1 time.
✓ Branch 10 → 6 taken 1 time.
✗ Branch 10 → 11 not taken.
2 while (idx <= last && state_is_valid(states[idx])) {
326 1 idx++;
327 }
328
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 15 taken 1 time.
1 if (idx > last) {
329 break;
330 }
331
332 // Go to line before first hole
333 idx--;
334 block_iter_move_down(bi, idx - current_line);
335 current_line = idx;
336
337 // NOTE: might not fill entire hole, which is ok
338 ssize_t count = fill_hole(syn, s, sm, bi, idx, last);
339 idx += count;
340 current_line += count;
341 }
342
343 // Add new
344 1 block_iter_move_down(bi, s->count - 1 - current_line);
345
2/2
✓ Branch 21 → 17 taken 7 times.
✓ Branch 21 → 22 taken 1 time.
8 while (s->count - 1 < line_nr) {
346 7 StringView line = block_iter_get_line_with_nl(bi);
347 7 highlight_line (
348 syn,
349 7 states[s->count - 1],
350 sm,
351 line,
352 7 &states[s->count]
353 );
354 7 s->count++;
355 7 block_iter_eat_line(bi);
356 }
357 }
358
359 1 const TermStyle **hl_line (
360 Syntax *syn,
361 PointerArray *line_start_states,
362 const StyleMap *sm,
363 StringView line,
364 size_t line_nr,
365 bool *next_changed
366 ) {
367 1 *next_changed = false;
368
1/2
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 14 not taken.
1 if (!syn) {
369 return NULL;
370 }
371
372 1 PointerArray *s = line_start_states;
373 1 BUG_ON(line_nr >= s->count);
374 1 State *next;
375 1 const TermStyle **styles = highlight_line(syn, s->ptrs[line_nr++], sm, line, &next);
376
377
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 9 taken 1 time.
1 if (line_nr == s->count) {
378 resize_line_states(s, s->count + 1);
379 s->ptrs[s->count++] = next;
380 *next_changed = true;
381
1/2
✓ Branch 9 → 10 taken 1 time.
✗ Branch 9 → 14 not taken.
1 } else if (s->ptrs[line_nr] == next) {
382 // Was not invalidated and didn't change
383
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 1 time.
1 } else if (states_equal(s->ptrs, line_nr, next)) {
384 // Was invalidated and didn't change
385 s->ptrs[line_nr] = next;
386 // *next_changed = 1;
387 } else {
388 // Invalidated or not but changed anyway
389 1 s->ptrs[line_nr] = next;
390 1 *next_changed = true;
391
1/2
✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 14 not taken.
1 if (line_nr + 1 < s->count) {
392 1 mark_state_invalid(s->ptrs, line_nr + 1);
393 }
394 }
395 return styles;
396 }
397
398 // Called after text has been inserted to re-highlight changed lines
399 2 void hl_insert(PointerArray *line_start_states, size_t first, size_t lines)
400 {
401 2 PointerArray *s = line_start_states;
402
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 13 taken 1 time.
2 if (first >= s->count) {
403 // Nothing to re-highlight
404 return;
405 }
406
407 1 size_t last = first + lines;
408
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 5 not taken.
1 if (last + 1 >= s->count) {
409 // Last already highlighted lines changed; there's nothing to
410 // gain, so throw them away
411 1 s->count = first + 1;
412 1 return;
413 }
414
415 // Add room for new line states
416 if (lines) {
417 size_t to = last + 1;
418 size_t from = first + 1;
419 resize_line_states(s, s->count + lines);
420 move_line_states(s, to, from, s->count - from);
421 s->count += lines;
422 }
423
424 // Invalidate start states of new and changed lines
425 for (size_t i = first + 1; i <= last + 1; i++) {
426 mark_state_invalid(s->ptrs, i);
427 }
428 }
429
430 // Called after text has been deleted to re-highlight changed lines
431 void hl_delete(PointerArray *line_start_states, size_t first, size_t lines)
432 {
433 PointerArray *s = line_start_states;
434 if (s->count == 1) {
435 return;
436 }
437
438 if (first >= s->count) {
439 // Nothing to highlight
440 return;
441 }
442
443 size_t last = first + lines;
444 if (last + 1 >= s->count) {
445 // Last already highlighted lines changed; there's nothing to
446 // gain, so throw them away
447 s->count = first + 1;
448 return;
449 }
450
451 // There are already highlighted lines after changed lines; try to
452 // save the work
453
454 // Remove deleted lines (states)
455 if (lines) {
456 size_t to = first + 1;
457 size_t from = last + 1;
458 move_line_states(s, to, from, s->count - from);
459 s->count -= lines;
460 }
461
462 // Invalidate line start state after the changed line
463 mark_state_invalid(s->ptrs, first + 1);
464 }
465