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