| 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 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 | // 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 0 (2→3) taken 7 times.
✓ Branch 1 (2→4) taken 1 times.
|
8 | if (len > alloc) { |
| 91 | 1 | alloc = next_multiple(len, 128); | |
| 92 | 1 | styles = xrenew(styles, alloc); | |
| 93 | } | ||
| 94 | |||
| 95 | 7 | top: | |
| 96 |
2/2✓ Branch 0 (8→9) taken 8 times.
✓ Branch 1 (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 0 (65→13) taken 918 times.
✓ Branch 1 (65→66) taken 104 times.
|
1022 | for (size_t ci = 0, n = state->conds.count; ci < n; ci++) { |
| 103 | 918 | const Condition *cond = state->conds.ptrs[ci]; | |
| 104 | 918 | const ConditionData *u = &cond->u; | |
| 105 | 918 | const ConditionType condtype = cond->type; | |
| 106 | 918 | const TermStyle *style = cond->a.emit_style; | |
| 107 | 918 | State *dest = cond->a.destination; | |
| 108 |
6/13✓ 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→39) not taken.
✗ Branch 7 (13→40) not taken.
✗ Branch 8 (13→42) not taken.
✗ Branch 9 (13→47) not taken.
✓ Branch 10 (13→52) taken 74 times.
✗ Branch 11 (13→56) not taken.
✗ Branch 12 (13→62) not taken.
|
918 | switch (condtype) { |
| 109 | 225 | case COND_CHAR_BUFFER: | |
| 110 |
2/2✓ Branch 0 (14→15) taken 82 times.
✓ Branch 1 (14→64) taken 143 times.
|
225 | if (!bitset_contains(u->bitset, line[i])) { |
| 111 | break; | ||
| 112 | } | ||
| 113 |
2/2✓ Branch 0 (15→16) taken 14 times.
✓ Branch 1 (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 0 (18→19) taken 1 times.
✗ Branch 1 (18→64) not taken.
✓ Branch 2 (20→21) taken 1 times.
✗ Branch 3 (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 0 (28→29) taken 19 times.
✓ Branch 1 (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 0 (30→31) taken 28 times.
✓ Branch 1 (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 0 (32→33) taken 75 times.
✓ Branch 1 (32→64) taken 3 times.
✓ Branch 2 (34→35) taken 8 times.
✓ Branch 3 (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 0 (36→37) not taken.
✓ Branch 1 (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 | 74 | case COND_STR2: | |
| 192 | // Optimized COND_STR (length 2, case sensitive) | ||
| 193 |
2/4✓ Branch 0 (52→53) taken 74 times.
✗ Branch 1 (52→64) not taken.
✗ Branch 2 (54→55) not taken.
✓ Branch 3 (54→64) taken 74 times.
|
74 | 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 | ✗ | const char *str = u->heredocend.data; | |
| 203 | ✗ | size_t slen = u->heredocend.length; | |
| 204 | ✗ | size_t end = i + slen; | |
| 205 | ✗ | if (len >= end && (slen == 0 || mem_equal(str, line + i, slen))) { | |
| 206 | ✗ | i += set_style_range(styles, style, i, end); | |
| 207 | ✗ | sidx = -1; | |
| 208 | ✗ | state = dest; | |
| 209 | ✗ | goto top; | |
| 210 | } | ||
| 211 | } break; | ||
| 212 | ✗ | default: | |
| 213 | − | BUG("unhandled condition type"); | |
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 |
3/5✓ Branch 0 (66→67) taken 18 times.
✓ Branch 1 (66→68) taken 82 times.
✓ Branch 2 (66→69) taken 4 times.
✗ Branch 3 (66→70) not taken.
✗ Branch 4 (66→73) not taken.
|
104 | switch (state->type) { |
| 218 | 82 | case STATE_EAT: | |
| 219 | 82 | styles[i++] = state->default_action.emit_style; | |
| 220 | // Fallthrough | ||
| 221 | case STATE_NOEAT: | ||
| 222 | sidx = -1; | ||
| 223 | // Fallthrough | ||
| 224 | 104 | case STATE_NOEAT_BUFFER: | |
| 225 | 104 | state = state->default_action.destination; | |
| 226 | 104 | break; | |
| 227 | ✗ | case STATE_HEREDOCBEGIN: | |
| 228 | ✗ | if (sidx < 0) { | |
| 229 | ✗ | sidx = i; | |
| 230 | } | ||
| 231 | ✗ | state = handle_heredoc(syn, state, sm, line + sidx, i - sidx); | |
| 232 | ✗ | break; | |
| 233 | ✗ | case STATE_INVALID: | |
| 234 | default: | ||
| 235 | − | BUG("unhandled default action type"); | |
| 236 | } | ||
| 237 | |||
| 238 | 104 | goto top; | |
| 239 | } | ||
| 240 | |||
| 241 | 1 | static void resize_line_states(PointerArray *s, size_t count) | |
| 242 | { | ||
| 243 |
1/2✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→5) taken 1 times.
|
1 | if (s->alloc < count) { |
| 244 | ✗ | s->alloc = next_multiple(count, 64); | |
| 245 | ✗ | s->ptrs = xrenew(s->ptrs, s->alloc); | |
| 246 | } | ||
| 247 | 1 | } | |
| 248 | |||
| 249 | ✗ | static void move_line_states ( | |
| 250 | PointerArray *s, | ||
| 251 | size_t to, | ||
| 252 | size_t from, | ||
| 253 | size_t count | ||
| 254 | ) { | ||
| 255 | ✗ | memmove(s->ptrs + to, s->ptrs + from, count * sizeof(*s->ptrs)); | |
| 256 | ✗ | } | |
| 257 | |||
| 258 | 1 | static void block_iter_move_down(BlockIter *bi, size_t count) | |
| 259 | { | ||
| 260 |
1/2✗ Branch 0 (4→3) not taken.
✓ Branch 1 (4→5) taken 1 times.
|
1 | while (count--) { |
| 261 | ✗ | block_iter_eat_line(bi); | |
| 262 | } | ||
| 263 | 1 | } | |
| 264 | |||
| 265 | ✗ | static ssize_t fill_hole ( | |
| 266 | Syntax *syn, | ||
| 267 | PointerArray *line_start_states, | ||
| 268 | const StyleMap *sm, | ||
| 269 | BlockIter *bi, | ||
| 270 | ssize_t sidx, | ||
| 271 | ssize_t eidx | ||
| 272 | ) { | ||
| 273 | ✗ | void **ptrs = line_start_states->ptrs; | |
| 274 | ✗ | ssize_t idx = sidx; | |
| 275 | |||
| 276 | ✗ | while (idx < eidx) { | |
| 277 | ✗ | State *st; | |
| 278 | ✗ | StringView line = block_iter_get_line_with_nl(bi); | |
| 279 | ✗ | block_iter_eat_line(bi); | |
| 280 | ✗ | highlight_line(syn, ptrs[idx++], sm, line, &st); | |
| 281 | |||
| 282 | ✗ | if (ptrs[idx] == st) { | |
| 283 | // Was not invalidated and didn't change | ||
| 284 | break; | ||
| 285 | } | ||
| 286 | |||
| 287 | ✗ | if (states_equal(ptrs, idx, st)) { | |
| 288 | // Was invalidated and didn't change | ||
| 289 | ✗ | ptrs[idx] = st; | |
| 290 | } else { | ||
| 291 | // Invalidated or not but changed anyway | ||
| 292 | ✗ | ptrs[idx] = st; | |
| 293 | ✗ | if (idx == eidx) { | |
| 294 | ✗ | mark_state_invalid(ptrs, idx + 1); | |
| 295 | } | ||
| 296 | } | ||
| 297 | } | ||
| 298 | ✗ | return idx - sidx; | |
| 299 | } | ||
| 300 | |||
| 301 | 1 | void hl_fill_start_states ( | |
| 302 | Syntax *syn, | ||
| 303 | PointerArray *line_start_states, | ||
| 304 | const StyleMap *sm, | ||
| 305 | BlockIter *bi, | ||
| 306 | size_t line_nr | ||
| 307 | ) { | ||
| 308 |
1/2✓ Branch 0 (2→3) taken 1 times.
✗ Branch 1 (2→22) not taken.
|
1 | if (!syn) { |
| 309 | return; | ||
| 310 | } | ||
| 311 | |||
| 312 | 1 | PointerArray *s = line_start_states; | |
| 313 | 1 | ssize_t current_line = 0; | |
| 314 | 1 | ssize_t idx = 0; | |
| 315 | |||
| 316 | // NOTE: "+ 2" so that you don't have to worry about overflow in fill_hole() | ||
| 317 | 1 | resize_line_states(s, line_nr + 2); | |
| 318 | 1 | State **states = (State **)s->ptrs; | |
| 319 | |||
| 320 | // Update invalid | ||
| 321 | 1 | ssize_t last = line_nr; | |
| 322 |
1/2✓ Branch 0 (4→5) taken 1 times.
✗ Branch 1 (4→7) not taken.
|
1 | if (last >= s->count) { |
| 323 | 1 | last = s->count - 1; | |
| 324 | } | ||
| 325 | ✗ | while (1) { | |
| 326 |
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])) { |
| 327 | 1 | idx++; | |
| 328 | } | ||
| 329 |
1/2✗ Branch 0 (11→12) not taken.
✓ Branch 1 (11→15) taken 1 times.
|
1 | if (idx > last) { |
| 330 | break; | ||
| 331 | } | ||
| 332 | |||
| 333 | // Go to line before first hole | ||
| 334 | ✗ | idx--; | |
| 335 | ✗ | block_iter_move_down(bi, idx - current_line); | |
| 336 | ✗ | current_line = idx; | |
| 337 | |||
| 338 | // NOTE: might not fill entire hole, which is ok | ||
| 339 | ✗ | ssize_t count = fill_hole(syn, s, sm, bi, idx, last); | |
| 340 | ✗ | idx += count; | |
| 341 | ✗ | current_line += count; | |
| 342 | } | ||
| 343 | |||
| 344 | // Add new | ||
| 345 | 1 | block_iter_move_down(bi, s->count - 1 - current_line); | |
| 346 |
2/2✓ Branch 0 (21→17) taken 7 times.
✓ Branch 1 (21→22) taken 1 times.
|
8 | while (s->count - 1 < line_nr) { |
| 347 | 7 | StringView line = block_iter_get_line_with_nl(bi); | |
| 348 | 7 | highlight_line ( | |
| 349 | syn, | ||
| 350 | 7 | states[s->count - 1], | |
| 351 | sm, | ||
| 352 | line, | ||
| 353 | 7 | &states[s->count] | |
| 354 | ); | ||
| 355 | 7 | s->count++; | |
| 356 | 7 | block_iter_eat_line(bi); | |
| 357 | } | ||
| 358 | } | ||
| 359 | |||
| 360 | 1 | const TermStyle **hl_line ( | |
| 361 | Syntax *syn, | ||
| 362 | PointerArray *line_start_states, | ||
| 363 | const StyleMap *sm, | ||
| 364 | StringView line, | ||
| 365 | size_t line_nr, | ||
| 366 | bool *next_changed | ||
| 367 | ) { | ||
| 368 | 1 | *next_changed = false; | |
| 369 |
1/2✓ Branch 0 (2→3) taken 1 times.
✗ Branch 1 (2→14) not taken.
|
1 | if (!syn) { |
| 370 | return NULL; | ||
| 371 | } | ||
| 372 | |||
| 373 | 1 | PointerArray *s = line_start_states; | |
| 374 | 1 | BUG_ON(line_nr >= s->count); | |
| 375 | 1 | State *next; | |
| 376 | 1 | const TermStyle **styles = highlight_line(syn, s->ptrs[line_nr++], sm, line, &next); | |
| 377 | |||
| 378 |
1/2✗ Branch 0 (6→7) not taken.
✓ Branch 1 (6→9) taken 1 times.
|
1 | if (line_nr == s->count) { |
| 379 | ✗ | resize_line_states(s, s->count + 1); | |
| 380 | ✗ | s->ptrs[s->count++] = next; | |
| 381 | ✗ | *next_changed = true; | |
| 382 |
1/2✓ Branch 0 (9→10) taken 1 times.
✗ Branch 1 (9→14) not taken.
|
1 | } else if (s->ptrs[line_nr] == next) { |
| 383 | // Was not invalidated and didn't change | ||
| 384 |
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)) { |
| 385 | // Was invalidated and didn't change | ||
| 386 | ✗ | s->ptrs[line_nr] = next; | |
| 387 | // *next_changed = 1; | ||
| 388 | } else { | ||
| 389 | // Invalidated or not but changed anyway | ||
| 390 | 1 | s->ptrs[line_nr] = next; | |
| 391 | 1 | *next_changed = true; | |
| 392 |
1/2✓ Branch 0 (12→13) taken 1 times.
✗ Branch 1 (12→14) not taken.
|
1 | if (line_nr + 1 < s->count) { |
| 393 | 1 | mark_state_invalid(s->ptrs, line_nr + 1); | |
| 394 | } | ||
| 395 | } | ||
| 396 | return styles; | ||
| 397 | } | ||
| 398 | |||
| 399 | // Called after text has been inserted to re-highlight changed lines | ||
| 400 | 2 | void hl_insert(PointerArray *line_start_states, size_t first, size_t lines) | |
| 401 | { | ||
| 402 | 2 | PointerArray *s = line_start_states; | |
| 403 |
2/2✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→13) taken 1 times.
|
2 | if (first >= s->count) { |
| 404 | // Nothing to re-highlight | ||
| 405 | return; | ||
| 406 | } | ||
| 407 | |||
| 408 | 1 | size_t last = first + lines; | |
| 409 |
1/2✓ Branch 0 (3→4) taken 1 times.
✗ Branch 1 (3→5) not taken.
|
1 | if (last + 1 >= s->count) { |
| 410 | // Last already highlighted lines changed; there's nothing to | ||
| 411 | // gain, so throw them away | ||
| 412 | 1 | s->count = first + 1; | |
| 413 | 1 | return; | |
| 414 | } | ||
| 415 | |||
| 416 | // Add room for new line states | ||
| 417 | ✗ | if (lines) { | |
| 418 | ✗ | size_t to = last + 1; | |
| 419 | ✗ | size_t from = first + 1; | |
| 420 | ✗ | resize_line_states(s, s->count + lines); | |
| 421 | ✗ | move_line_states(s, to, from, s->count - from); | |
| 422 | ✗ | s->count += lines; | |
| 423 | } | ||
| 424 | |||
| 425 | // Invalidate start states of new and changed lines | ||
| 426 | ✗ | for (size_t i = first + 1; i <= last + 1; i++) { | |
| 427 | ✗ | mark_state_invalid(s->ptrs, i); | |
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | // Called after text has been deleted to re-highlight changed lines | ||
| 432 | ✗ | void hl_delete(PointerArray *line_start_states, size_t first, size_t lines) | |
| 433 | { | ||
| 434 | ✗ | PointerArray *s = line_start_states; | |
| 435 | ✗ | if (s->count == 1) { | |
| 436 | return; | ||
| 437 | } | ||
| 438 | |||
| 439 | ✗ | if (first >= s->count) { | |
| 440 | // Nothing to highlight | ||
| 441 | return; | ||
| 442 | } | ||
| 443 | |||
| 444 | ✗ | size_t last = first + lines; | |
| 445 | ✗ | if (last + 1 >= s->count) { | |
| 446 | // Last already highlighted lines changed; there's nothing to | ||
| 447 | // gain, so throw them away | ||
| 448 | ✗ | s->count = first + 1; | |
| 449 | ✗ | return; | |
| 450 | } | ||
| 451 | |||
| 452 | // There are already highlighted lines after changed lines; try to | ||
| 453 | // save the work | ||
| 454 | |||
| 455 | // Remove deleted lines (states) | ||
| 456 | ✗ | if (lines) { | |
| 457 | ✗ | size_t to = first + 1; | |
| 458 | ✗ | size_t from = last + 1; | |
| 459 | ✗ | move_line_states(s, to, from, s->count - from); | |
| 460 | ✗ | s->count -= lines; | |
| 461 | } | ||
| 462 | |||
| 463 | // Invalidate line start state after the changed line | ||
| 464 | ✗ | mark_state_invalid(s->ptrs, first + 1); | |
| 465 | } | ||
| 466 |