syntax.c (182289B)
1 // syntax.c: code for syntax highlighting 2 3 #include <assert.h> 4 #include <inttypes.h> 5 #include <stdbool.h> 6 #include <stddef.h> 7 #include <stdlib.h> 8 #include <string.h> 9 10 #include "nvim/ascii_defs.h" 11 #include "nvim/autocmd.h" 12 #include "nvim/autocmd_defs.h" 13 #include "nvim/buffer.h" 14 #include "nvim/buffer_defs.h" 15 #include "nvim/charset.h" 16 #include "nvim/cmdexpand_defs.h" 17 #include "nvim/drawscreen.h" 18 #include "nvim/errors.h" 19 #include "nvim/eval/typval_defs.h" 20 #include "nvim/eval/vars.h" 21 #include "nvim/ex_cmds_defs.h" 22 #include "nvim/ex_docmd.h" 23 #include "nvim/fold.h" 24 #include "nvim/garray.h" 25 #include "nvim/garray_defs.h" 26 #include "nvim/gettext_defs.h" 27 #include "nvim/globals.h" 28 #include "nvim/hashtab.h" 29 #include "nvim/hashtab_defs.h" 30 #include "nvim/highlight_defs.h" 31 #include "nvim/highlight_group.h" 32 #include "nvim/indent_c.h" 33 #include "nvim/macros_defs.h" 34 #include "nvim/mbyte.h" 35 #include "nvim/memline.h" 36 #include "nvim/memory.h" 37 #include "nvim/message.h" 38 #include "nvim/option_vars.h" 39 #include "nvim/optionstr.h" 40 #include "nvim/os/input.h" 41 #include "nvim/path.h" 42 #include "nvim/pos_defs.h" 43 #include "nvim/profile.h" 44 #include "nvim/regexp.h" 45 #include "nvim/regexp_defs.h" 46 #include "nvim/runtime.h" 47 #include "nvim/strings.h" 48 #include "nvim/syntax.h" 49 #include "nvim/types_defs.h" 50 #include "nvim/vim_defs.h" 51 52 static bool did_syntax_onoff = false; 53 54 // different types of offsets that are possible 55 #define SPO_MS_OFF 0 // match start offset 56 #define SPO_ME_OFF 1 // match end offset 57 #define SPO_HS_OFF 2 // highl. start offset 58 #define SPO_HE_OFF 3 // highl. end offset 59 #define SPO_RS_OFF 4 // region start offset 60 #define SPO_RE_OFF 5 // region end offset 61 #define SPO_LC_OFF 6 // leading context offset 62 #define SPO_COUNT 7 63 64 static const char e_illegal_arg[] = N_("E390: Illegal argument: %s"); 65 static const char e_contains_argument_not_accepted_here[] 66 = N_("E395: Contains argument not accepted here"); 67 static const char e_invalid_cchar_value[] 68 = N_("E844: Invalid cchar value"); 69 static const char e_trailing_char_after_rsb_str_str[] 70 = N_("E890: Trailing char after ']': %s]%s"); 71 72 // The patterns that are being searched for are stored in a syn_pattern. 73 // A match item consists of one pattern. 74 // A start/end item consists of n start patterns and m end patterns. 75 // A start/skip/end item consists of n start patterns, one skip pattern and m 76 // end patterns. 77 // For the latter two, the patterns are always consecutive: start-skip-end. 78 // 79 // A character offset can be given for the matched text (_m_start and _m_end) 80 // and for the actually highlighted text (_h_start and _h_end). 81 // 82 // Note that ordering of members is optimized to reduce padding. 83 typedef struct { 84 char sp_type; // see SPTYPE_ defines below 85 bool sp_syncing; // this item used for syncing 86 int16_t sp_syn_match_id; // highlight group ID of pattern 87 int16_t sp_off_flags; // see below 88 int sp_offsets[SPO_COUNT]; // offsets 89 int sp_flags; // see HL_ defines below 90 int sp_cchar; // conceal substitute character 91 int sp_ic; // ignore-case flag for sp_prog 92 int sp_sync_idx; // sync item index (syncing only) 93 int sp_line_id; // ID of last line where tried 94 int sp_startcol; // next match in sp_line_id line 95 int16_t *sp_cont_list; // cont. group IDs, if non-zero 96 int16_t *sp_next_list; // next group IDs, if non-zero 97 struct sp_syn sp_syn; // struct passed to in_id_list() 98 char *sp_pattern; // regexp to match, pattern 99 regprog_T *sp_prog; // regexp to match, program 100 syn_time_T sp_time; 101 } synpat_T; 102 103 typedef struct { 104 char *scl_name; // syntax cluster name 105 char *scl_name_u; // uppercase of scl_name 106 int16_t *scl_list; // IDs in this syntax cluster 107 } syn_cluster_T; 108 109 // For the current state we need to remember more than just the idx. 110 // When si_m_endpos.lnum is 0, the items other than si_idx are unknown. 111 // (The end positions have the column number of the next char) 112 typedef struct { 113 int si_idx; // index of syntax pattern or 114 // KEYWORD_IDX 115 int si_id; // highlight group ID for keywords 116 int si_trans_id; // idem, transparency removed 117 int si_m_lnum; // lnum of the match 118 int si_m_startcol; // starting column of the match 119 lpos_T si_m_endpos; // just after end posn of the match 120 lpos_T si_h_startpos; // start position of the highlighting 121 lpos_T si_h_endpos; // end position of the highlighting 122 lpos_T si_eoe_pos; // end position of end pattern 123 int si_end_idx; // group ID for end pattern or zero 124 int si_ends; // if match ends before si_m_endpos 125 int si_attr; // attributes in this state 126 int si_flags; // HL_HAS_EOL flag in this state, and 127 // HL_SKIP* for si_next_list 128 int si_seqnr; // sequence number 129 int si_cchar; // substitution character for conceal 130 int16_t *si_cont_list; // list of contained groups 131 int16_t *si_next_list; // nextgroup IDs after this item ends 132 reg_extmatch_T *si_extmatch; // \z(...\) matches from start 133 // pattern 134 } stateitem_T; 135 136 // Struct to reduce the number of arguments to get_syn_options(), it's used 137 // very often. 138 typedef struct { 139 int flags; // flags for contained and transparent 140 bool keyword; // true for ":syn keyword" 141 int *sync_idx; // syntax item for "grouphere" argument, NULL 142 // if not allowed 143 bool has_cont_list; // true if "cont_list" can be used 144 int16_t *cont_list; // group IDs for "contains" argument 145 int16_t *cont_in_list; // group IDs for "containedin" argument 146 int16_t *next_list; // group IDs for "nextgroup" argument 147 } syn_opt_arg_T; 148 149 typedef struct { 150 proftime_T total; 151 int count; 152 int match; 153 proftime_T slowest; 154 proftime_T average; 155 int id; 156 char *pattern; 157 } time_entry_T; 158 159 #include "syntax.c.generated.h" 160 161 static char *(spo_name_tab[SPO_COUNT]) = 162 { "ms=", "me=", "hs=", "he=", "rs=", "re=", "lc=" }; 163 164 // The sp_off_flags are computed like this: 165 // offset from the start of the matched text: (1 << SPO_XX_OFF) 166 // offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT)) 167 // When both are present, only one is used. 168 169 #define SPTYPE_MATCH 1 // match keyword with this group ID 170 #define SPTYPE_START 2 // match a regexp, start of item 171 #define SPTYPE_END 3 // match a regexp, end of item 172 #define SPTYPE_SKIP 4 // match a regexp, skip within item 173 174 #define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data)) 175 176 #define NONE_IDX (-2) // value of sp_sync_idx for "NONE" 177 178 // Flags for b_syn_sync_flags: 179 #define SF_CCOMMENT 0x01 // sync on a C-style comment 180 #define SF_MATCH 0x02 // sync by matching a pattern 181 182 #define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data)) 183 184 #define MAXKEYWLEN 80 // maximum length of a keyword 185 186 // The attributes of the syntax item that has been recognized. 187 static int current_attr = 0; // attr of current syntax word 188 static int current_id = 0; // ID of current char for syn_get_id() 189 static int current_trans_id = 0; // idem, transparency removed 190 static int current_flags = 0; 191 static int current_seqnr = 0; 192 static int current_sub_char = 0; 193 194 // Methods of combining two clusters 195 #define CLUSTER_REPLACE 1 // replace first list with second 196 #define CLUSTER_ADD 2 // add second list to first 197 #define CLUSTER_SUBTRACT 3 // subtract second list from first 198 199 #define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data)) 200 201 // Syntax group IDs have different types: 202 // 0 - 19999 normal syntax groups 203 // 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added) 204 // 21000 - 21999 TOP indicator (current_syn_inc_tag added) 205 // 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added) 206 // 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID) 207 #define SYNID_ALLBUT MAX_HL_ID // syntax group ID for contains=ALLBUT 208 #define SYNID_TOP 21000 // syntax group ID for contains=TOP 209 #define SYNID_CONTAINED 22000 // syntax group ID for contains=CONTAINED 210 #define SYNID_CLUSTER 23000 // first syntax group ID for clusters 211 212 #define MAX_SYN_INC_TAG 999 // maximum before the above overflow 213 #define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER) 214 215 // Annoying Hack(TM): ":syn include" needs this pointer to pass to 216 // expand_filename(). Most of the other syntax commands don't need it, so 217 // instead of passing it to them, we stow it here. 218 static char **syn_cmdlinep; 219 220 // Another Annoying Hack(TM): To prevent rules from other ":syn include"'d 221 // files from leaking into ALLBUT lists, we assign a unique ID to the 222 // rules in each ":syn include"'d file. 223 static int current_syn_inc_tag = 0; 224 static int running_syn_inc_tag = 0; 225 226 // In a hashtable item "hi_key" points to "keyword" in a keyentry. 227 // This avoids adding a pointer to the hashtable item. 228 // KE2HIKEY() converts a var pointer to a hashitem key pointer. 229 // HIKEY2KE() converts a hashitem key pointer to a var pointer. 230 // HI2KE() converts a hashitem pointer to a var pointer. 231 static keyentry_T dumkey; 232 #define KE2HIKEY(kp) ((kp)->keyword) 233 #define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char *)&dumkey))) 234 #define HI2KE(hi) HIKEY2KE((hi)->hi_key) 235 236 // To reduce the time spent in keepend(), remember at which level in the state 237 // stack the first item with "keepend" is present. When "-1", there is no 238 // "keepend" on the stack. 239 static int keepend_level = -1; 240 241 static char msg_no_items[] = N_("No Syntax items defined for this buffer"); 242 243 // value of si_idx for keywords 244 #define KEYWORD_IDX (-1) 245 // valid of si_cont_list for containing all but contained groups 246 #define ID_LIST_ALL ((int16_t *)-1) 247 248 static int next_seqnr = 1; // value to use for si_seqnr 249 250 // The next possible match in the current line for any pattern is remembered, 251 // to avoid having to try for a match in each column. 252 // If next_match_idx == -1, not tried (in this line) yet. 253 // If next_match_col == MAXCOL, no match found in this line. 254 // (All end positions have the column of the char after the end) 255 static int next_match_col; // column for start of next match 256 static lpos_T next_match_m_endpos; // position for end of next match 257 static lpos_T next_match_h_startpos; // pos. for highl. start of next match 258 static lpos_T next_match_h_endpos; // pos. for highl. end of next match 259 static int next_match_idx; // index of matched item 260 static int next_match_flags; // flags for next match 261 static lpos_T next_match_eos_pos; // end of start pattn (start region) 262 static lpos_T next_match_eoe_pos; // pos. for end of end pattern 263 static int next_match_end_idx; // ID of group for end pattn or zero 264 static reg_extmatch_T *next_match_extmatch = NULL; 265 266 // A state stack is an array of integers or stateitem_T, stored in a 267 // garray_T. A state stack is invalid if its itemsize entry is zero. 268 #define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0) 269 #define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0) 270 271 // The current state (within the line) of the recognition engine. 272 // When current_state.ga_itemsize is 0 the current state is invalid. 273 static win_T *syn_win; // current window for highlighting 274 static buf_T *syn_buf; // current buffer for highlighting 275 static synblock_T *syn_block; // current buffer for highlighting 276 static proftime_T *syn_tm; // timeout limit 277 static linenr_T current_lnum = 0; // lnum of current state 278 static colnr_T current_col = 0; // column of current state 279 static bool current_state_stored = false; // true if stored current state 280 // after setting current_finished 281 static bool current_finished = false; // current line has been finished 282 static garray_T current_state // current stack of state_items 283 = GA_EMPTY_INIT_VALUE; 284 static int16_t *current_next_list = NULL; // when non-zero, nextgroup list 285 static int current_next_flags = 0; // flags for current_next_list 286 static int current_line_id = 0; // unique number for current line 287 288 #define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx] 289 290 static bool syn_time_on = false; 291 #define IF_SYN_TIME(p) (p) 292 293 // Set the timeout used for syntax highlighting. 294 // Use NULL to reset, no timeout. 295 void syn_set_timeout(proftime_T *tm) 296 { 297 syn_tm = tm; 298 } 299 300 // Start the syntax recognition for a line. This function is normally called 301 // from the screen updating, once for each displayed line. 302 // The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get 303 // it. Careful: curbuf and curwin are likely to point to another buffer and 304 // window. 305 void syntax_start(win_T *wp, linenr_T lnum) 306 { 307 synstate_T *last_valid = NULL; 308 synstate_T *last_min_valid = NULL; 309 synstate_T *sp; 310 synstate_T *prev = NULL; 311 linenr_T first_stored; 312 int dist; 313 static varnumber_T changedtick = 0; // remember the last change ID 314 315 current_sub_char = NUL; 316 317 // After switching buffers, invalidate current_state. 318 // Also do this when a change was made, the current state may be invalid 319 // then. 320 if (syn_block != wp->w_s 321 || syn_buf != wp->w_buffer 322 || changedtick != buf_get_changedtick(syn_buf)) { 323 invalidate_current_state(); 324 syn_buf = wp->w_buffer; 325 syn_block = wp->w_s; 326 } 327 changedtick = buf_get_changedtick(syn_buf); 328 syn_win = wp; 329 330 // Allocate syntax stack when needed. 331 syn_stack_alloc(); 332 if (syn_block->b_sst_array == NULL) { 333 return; // out of memory 334 } 335 syn_block->b_sst_lasttick = display_tick; 336 337 // If the state of the end of the previous line is useful, store it. 338 if (VALID_STATE(¤t_state) 339 && current_lnum < lnum 340 && current_lnum < syn_buf->b_ml.ml_line_count) { 341 syn_finish_line(false); 342 if (!current_state_stored) { 343 current_lnum++; 344 store_current_state(); 345 } 346 347 // If the current_lnum is now the same as "lnum", keep the current 348 // state (this happens very often!). Otherwise invalidate 349 // current_state and figure it out below. 350 if (current_lnum != lnum) { 351 invalidate_current_state(); 352 } 353 } else { 354 invalidate_current_state(); 355 } 356 357 // Try to synchronize from a saved state in b_sst_array[]. 358 // Only do this if lnum is not before and not to far beyond a saved state. 359 if (INVALID_STATE(¤t_state) && syn_block->b_sst_array != NULL) { 360 // Find last valid saved state before start_lnum. 361 for (synstate_T *p = syn_block->b_sst_first; p != NULL; p = p->sst_next) { 362 if (p->sst_lnum > lnum) { 363 break; 364 } 365 if (p->sst_change_lnum == 0) { 366 last_valid = p; 367 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines) { 368 last_min_valid = p; 369 } 370 } 371 } 372 if (last_min_valid != NULL) { 373 load_current_state(last_min_valid); 374 } 375 } 376 377 // If "lnum" is before or far beyond a line with a saved state, need to 378 // re-synchronize. 379 if (INVALID_STATE(¤t_state)) { 380 syn_sync(wp, lnum, last_valid); 381 if (current_lnum == 1) { 382 // First line is always valid, no matter "minlines". 383 first_stored = 1; 384 } else { 385 // Need to parse "minlines" lines before state can be considered 386 // valid to store. 387 first_stored = current_lnum + syn_block->b_syn_sync_minlines; 388 } 389 } else { 390 first_stored = current_lnum; 391 } 392 393 // Advance from the sync point or saved state until the current line. 394 // Save some entries for syncing with later on. 395 if (syn_block->b_sst_len <= Rows) { 396 dist = 999999; 397 } else { 398 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1; 399 } 400 while (current_lnum < lnum) { 401 syn_start_line(); 402 syn_finish_line(false); 403 current_lnum++; 404 405 // If we parsed at least "minlines" lines or started at a valid 406 // state, the current state is considered valid. 407 if (current_lnum >= first_stored) { 408 // Check if the saved state entry is for the current line and is 409 // equal to the current state. If so, then validate all saved 410 // states that depended on a change before the parsed line. 411 if (prev == NULL) { 412 prev = syn_stack_find_entry(current_lnum - 1); 413 } 414 if (prev == NULL) { 415 sp = syn_block->b_sst_first; 416 } else { 417 sp = prev; 418 } 419 while (sp != NULL && sp->sst_lnum < current_lnum) { 420 sp = sp->sst_next; 421 } 422 if (sp != NULL 423 && sp->sst_lnum == current_lnum 424 && syn_stack_equal(sp)) { 425 linenr_T parsed_lnum = current_lnum; 426 prev = sp; 427 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum) { 428 if (sp->sst_lnum <= lnum) { 429 // valid state before desired line, use this one 430 prev = sp; 431 } else if (sp->sst_change_lnum == 0) { 432 // past saved states depending on change, break here. 433 break; 434 } 435 sp->sst_change_lnum = 0; 436 sp = sp->sst_next; 437 } 438 load_current_state(prev); 439 } else if (prev == NULL 440 // Store the state at this line when it's the first one, the line 441 // where we start parsing, or some distance from the previously 442 // saved state. But only when parsed at least 'minlines'. 443 || current_lnum == lnum 444 || current_lnum >= prev->sst_lnum + dist) { 445 prev = store_current_state(); 446 } 447 } 448 449 // This can take a long time: break when CTRL-C pressed. The current 450 // state will be wrong then. 451 line_breakcheck(); 452 if (got_int) { 453 current_lnum = lnum; 454 break; 455 } 456 } 457 458 syn_start_line(); 459 } 460 461 // We cannot simply discard growarrays full of state_items or buf_states; we 462 // have to manually release their extmatch pointers first. 463 static void clear_syn_state(synstate_T *p) 464 { 465 if (p->sst_stacksize > SST_FIX_STATES) { 466 #define UNREF_BUFSTATE_EXTMATCH(bs) unref_extmatch((bs)->bs_extmatch) 467 GA_DEEP_CLEAR(&(p->sst_union.sst_ga), bufstate_T, UNREF_BUFSTATE_EXTMATCH); 468 } else { 469 for (int i = 0; i < p->sst_stacksize; i++) { 470 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch); 471 } 472 } 473 } 474 475 // Cleanup the current_state stack. 476 static void clear_current_state(void) 477 { 478 #define UNREF_STATEITEM_EXTMATCH(si) unref_extmatch((si)->si_extmatch) 479 GA_DEEP_CLEAR(¤t_state, stateitem_T, UNREF_STATEITEM_EXTMATCH); 480 } 481 482 // Try to find a synchronisation point for line "lnum". 483 // 484 // This sets current_lnum and the current state. One of three methods is 485 // used: 486 // 1. Search backwards for the end of a C-comment. 487 // 2. Search backwards for given sync patterns. 488 // 3. Simply start on a given number of lines above "lnum". 489 static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) 490 { 491 pos_T cursor_save; 492 linenr_T lnum; 493 linenr_T break_lnum; 494 stateitem_T *cur_si; 495 synpat_T *spp; 496 int found_flags = 0; 497 int found_match_idx = 0; 498 linenr_T found_current_lnum = 0; 499 int found_current_col = 0; 500 lpos_T found_m_endpos; 501 502 // Clear any current state that might be hanging around. 503 invalidate_current_state(); 504 505 // Start at least "minlines" back. Default starting point for parsing is 506 // there. 507 // Start further back, to avoid that scrolling backwards will result in 508 // resyncing for every line. Now it resyncs only one out of N lines, 509 // where N is minlines * 1.5, or minlines * 2 if minlines is small. 510 // Watch out for overflow when minlines is MAXLNUM. 511 if (syn_block->b_syn_sync_minlines > start_lnum) { 512 start_lnum = 1; 513 } else { 514 if (syn_block->b_syn_sync_minlines == 1) { 515 lnum = 1; 516 } else if (syn_block->b_syn_sync_minlines < 10) { 517 lnum = syn_block->b_syn_sync_minlines * 2; 518 } else { 519 lnum = syn_block->b_syn_sync_minlines * 3 / 2; 520 } 521 if (syn_block->b_syn_sync_maxlines != 0 522 && lnum > syn_block->b_syn_sync_maxlines) { 523 lnum = syn_block->b_syn_sync_maxlines; 524 } 525 if (lnum >= start_lnum) { 526 start_lnum = 1; 527 } else { 528 start_lnum -= lnum; 529 } 530 } 531 current_lnum = start_lnum; 532 533 // 1. Search backwards for the end of a C-style comment. 534 if (syn_block->b_syn_sync_flags & SF_CCOMMENT) { 535 // Need to make syn_buf the current buffer for a moment, to be able to 536 // use find_start_comment(). 537 win_T *curwin_save = curwin; 538 curwin = wp; 539 buf_T *curbuf_save = curbuf; 540 curbuf = syn_buf; 541 542 // Skip lines that end in a backslash. 543 for (; start_lnum > 1; start_lnum--) { 544 char *l = ml_get(start_lnum - 1); 545 if (*l == NUL || *(l + ml_get_len(start_lnum - 1) - 1) != '\\') { 546 break; 547 } 548 } 549 current_lnum = start_lnum; 550 551 // set cursor to start of search 552 cursor_save = wp->w_cursor; 553 wp->w_cursor.lnum = start_lnum; 554 wp->w_cursor.col = 0; 555 556 // If the line is inside a comment, need to find the syntax item that 557 // defines the comment. 558 // Restrict the search for the end of a comment to b_syn_sync_maxlines. 559 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL) { 560 for (int idx = syn_block->b_syn_patterns.ga_len; --idx >= 0;) { 561 if (SYN_ITEMS(syn_block)[idx].sp_syn.id 562 == syn_block->b_syn_sync_id 563 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START) { 564 validate_current_state(); 565 push_current_state(idx); 566 update_si_attr(current_state.ga_len - 1); 567 break; 568 } 569 } 570 } 571 572 // restore cursor and buffer 573 wp->w_cursor = cursor_save; 574 curwin = curwin_save; 575 curbuf = curbuf_save; 576 } else if (syn_block->b_syn_sync_flags & SF_MATCH) { 577 // 2. Search backwards for given sync patterns. 578 if (syn_block->b_syn_sync_maxlines != 0 579 && start_lnum > syn_block->b_syn_sync_maxlines) { 580 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines; 581 } else { 582 break_lnum = 0; 583 } 584 585 found_m_endpos.lnum = 0; 586 found_m_endpos.col = 0; 587 linenr_T end_lnum = start_lnum; 588 lnum = start_lnum; 589 while (--lnum > break_lnum) { 590 // This can take a long time: break when CTRL-C pressed. 591 line_breakcheck(); 592 if (got_int) { 593 invalidate_current_state(); 594 current_lnum = start_lnum; 595 break; 596 } 597 598 // Check if we have run into a valid saved state stack now. 599 if (last_valid != NULL && lnum == last_valid->sst_lnum) { 600 load_current_state(last_valid); 601 break; 602 } 603 604 // Check if the previous line has the line-continuation pattern. 605 if (lnum > 1 && syn_match_linecont(lnum - 1)) { 606 continue; 607 } 608 609 // Start with nothing on the state stack 610 validate_current_state(); 611 612 for (current_lnum = lnum; current_lnum < end_lnum; current_lnum++) { 613 syn_start_line(); 614 while (true) { 615 bool had_sync_point = syn_finish_line(true); 616 // When a sync point has been found, remember where, and 617 // continue to look for another one, further on in the line. 618 if (had_sync_point && current_state.ga_len) { 619 cur_si = &CUR_STATE(current_state.ga_len - 1); 620 if (cur_si->si_m_endpos.lnum > start_lnum) { 621 // ignore match that goes to after where started 622 current_lnum = end_lnum; 623 break; 624 } 625 if (cur_si->si_idx < 0) { 626 // Cannot happen? 627 found_flags = 0; 628 found_match_idx = KEYWORD_IDX; 629 } else { 630 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]); 631 found_flags = spp->sp_flags; 632 found_match_idx = spp->sp_sync_idx; 633 } 634 found_current_lnum = current_lnum; 635 found_current_col = current_col; 636 found_m_endpos = cur_si->si_m_endpos; 637 // Continue after the match (be aware of a zero-length 638 // match). 639 if (found_m_endpos.lnum > current_lnum) { 640 current_lnum = found_m_endpos.lnum; 641 current_col = found_m_endpos.col; 642 if (current_lnum >= end_lnum) { 643 break; 644 } 645 } else if (found_m_endpos.col > current_col) { 646 current_col = found_m_endpos.col; 647 } else { 648 current_col++; 649 } 650 651 // syn_current_attr() will have skipped the check for 652 // an item that ends here, need to do that now. Be 653 // careful not to go past the NUL. 654 colnr_T prev_current_col = current_col; 655 if (syn_getcurline()[current_col] != NUL) { 656 current_col++; 657 } 658 check_state_ends(); 659 current_col = prev_current_col; 660 } else { 661 break; 662 } 663 } 664 } 665 666 // If a sync point was encountered, break here. 667 if (found_flags) { 668 // Put the item that was specified by the sync point on the 669 // state stack. If there was no item specified, make the 670 // state stack empty. 671 clear_current_state(); 672 if (found_match_idx >= 0) { 673 push_current_state(found_match_idx); 674 update_si_attr(current_state.ga_len - 1); 675 } 676 677 // When using "grouphere", continue from the sync point 678 // match, until the end of the line. Parsing starts at 679 // the next line. 680 // For "groupthere" the parsing starts at start_lnum. 681 if (found_flags & HL_SYNC_HERE) { 682 if (!GA_EMPTY(¤t_state)) { 683 cur_si = &CUR_STATE(current_state.ga_len - 1); 684 cur_si->si_h_startpos.lnum = found_current_lnum; 685 cur_si->si_h_startpos.col = found_current_col; 686 update_si_end(cur_si, (int)current_col, true); 687 check_keepend(); 688 } 689 current_col = found_m_endpos.col; 690 current_lnum = found_m_endpos.lnum; 691 syn_finish_line(false); 692 current_lnum++; 693 } else { 694 current_lnum = start_lnum; 695 } 696 697 break; 698 } 699 700 end_lnum = lnum; 701 invalidate_current_state(); 702 } 703 704 // Ran into start of the file or exceeded maximum number of lines 705 if (lnum <= break_lnum) { 706 invalidate_current_state(); 707 current_lnum = break_lnum + 1; 708 } 709 } 710 711 validate_current_state(); 712 } 713 714 static void save_chartab(char *chartab) 715 { 716 if (syn_block->b_syn_isk == empty_string_option) { 717 return; 718 } 719 720 memmove(chartab, syn_buf->b_chartab, (size_t)32); 721 memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab, (size_t)32); 722 } 723 724 static void restore_chartab(char *chartab) 725 { 726 if (syn_win->w_s->b_syn_isk != empty_string_option) { 727 memmove(syn_buf->b_chartab, chartab, (size_t)32); 728 } 729 } 730 731 /// Return true if the line-continuation pattern matches in line "lnum". 732 static int syn_match_linecont(linenr_T lnum) 733 { 734 if (syn_block->b_syn_linecont_prog == NULL) { 735 return false; 736 } 737 738 regmmatch_T regmatch; 739 // chartab array for syn iskeyword 740 char buf_chartab[32]; 741 save_chartab(buf_chartab); 742 743 regmatch.rmm_ic = syn_block->b_syn_linecont_ic; 744 regmatch.regprog = syn_block->b_syn_linecont_prog; 745 int r = syn_regexec(®match, lnum, 0, 746 IF_SYN_TIME(&syn_block->b_syn_linecont_time)); 747 syn_block->b_syn_linecont_prog = regmatch.regprog; 748 749 restore_chartab(buf_chartab); 750 return r; 751 } 752 753 // Prepare the current state for the start of a line. 754 static void syn_start_line(void) 755 { 756 current_finished = false; 757 current_col = 0; 758 759 // Need to update the end of a start/skip/end that continues from the 760 // previous line and regions that have "keepend". 761 if (!GA_EMPTY(¤t_state)) { 762 syn_update_ends(true); 763 check_state_ends(); 764 } 765 766 next_match_idx = -1; 767 current_line_id++; 768 next_seqnr = 1; 769 } 770 771 /// Check for items in the stack that need their end updated. 772 /// 773 /// @param startofline if true the last item is always updated. 774 /// if false the item with "keepend" is forcefully updated. 775 static void syn_update_ends(bool startofline) 776 { 777 stateitem_T *cur_si; 778 779 if (startofline) { 780 // Check for a match carried over from a previous line with a 781 // contained region. The match ends as soon as the region ends. 782 for (int i = 0; i < current_state.ga_len; i++) { 783 cur_si = &CUR_STATE(i); 784 if (cur_si->si_idx >= 0 785 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type 786 == SPTYPE_MATCH 787 && cur_si->si_m_endpos.lnum < current_lnum) { 788 cur_si->si_flags |= HL_MATCHCONT; 789 cur_si->si_m_endpos.lnum = 0; 790 cur_si->si_m_endpos.col = 0; 791 cur_si->si_h_endpos = cur_si->si_m_endpos; 792 cur_si->si_ends = true; 793 } 794 } 795 } 796 797 // Need to update the end of a start/skip/end that continues from the 798 // previous line. And regions that have "keepend", because they may 799 // influence contained items. If we've just removed "extend" 800 // (startofline == 0) then we should update ends of normal regions 801 // contained inside "keepend" because "extend" could have extended 802 // these "keepend" regions as well as contained normal regions. 803 // Then check for items ending in column 0. 804 int i = current_state.ga_len - 1; 805 if (keepend_level >= 0) { 806 for (; i > keepend_level; i--) { 807 if (CUR_STATE(i).si_flags & HL_EXTEND) { 808 break; 809 } 810 } 811 } 812 813 bool seen_keepend = false; 814 for (; i < current_state.ga_len; i++) { 815 cur_si = &CUR_STATE(i); 816 if ((cur_si->si_flags & HL_KEEPEND) 817 || (seen_keepend && !startofline) 818 || (i == current_state.ga_len - 1 && startofline)) { 819 cur_si->si_h_startpos.col = 0; // start highl. in col 0 820 cur_si->si_h_startpos.lnum = current_lnum; 821 822 if (!(cur_si->si_flags & HL_MATCHCONT)) { 823 update_si_end(cur_si, (int)current_col, !startofline); 824 } 825 826 if (!startofline && (cur_si->si_flags & HL_KEEPEND)) { 827 seen_keepend = true; 828 } 829 } 830 } 831 check_keepend(); 832 } 833 834 ///////////////////////////////////////// 835 // Handling of the state stack cache. 836 837 // EXPLANATION OF THE SYNTAX STATE STACK CACHE 838 // 839 // To speed up syntax highlighting, the state stack for the start of some 840 // lines is cached. These entries can be used to start parsing at that point. 841 // 842 // The stack is kept in b_sst_array[] for each buffer. There is a list of 843 // valid entries. b_sst_first points to the first one, then follow sst_next. 844 // The entries are sorted on line number. The first entry is often for line 2 845 // (line 1 always starts with an empty stack). 846 // There is also a list for free entries. This construction is used to avoid 847 // having to allocate and free memory blocks too often. 848 // 849 // When making changes to the buffer, this is logged in b_mod_*. When calling 850 // update_screen() to update the display, it will call 851 // syn_stack_apply_changes() for each displayed buffer to adjust the cached 852 // entries. The entries which are inside the changed area are removed, 853 // because they must be recomputed. Entries below the changed have their line 854 // number adjusted for deleted/inserted lines, and have their sst_change_lnum 855 // set to indicate that a check must be made if the changed lines would change 856 // the cached entry. 857 // 858 // When later displaying lines, an entry is stored for each line. Displayed 859 // lines are likely to be displayed again, in which case the state at the 860 // start of the line is needed. 861 // For not displayed lines, an entry is stored for every so many lines. These 862 // entries will be used e.g., when scrolling backwards. The distance between 863 // entries depends on the number of lines in the buffer. For small buffers 864 // the distance is fixed at SST_DIST, for large buffers there is a fixed 865 // number of entries SST_MAX_ENTRIES, and the distance is computed. 866 867 static void syn_stack_free_block(synblock_T *block) 868 { 869 if (block->b_sst_array == NULL) { 870 return; 871 } 872 873 for (synstate_T *p = block->b_sst_first; p != NULL; p = p->sst_next) { 874 clear_syn_state(p); 875 } 876 XFREE_CLEAR(block->b_sst_array); 877 block->b_sst_first = NULL; 878 block->b_sst_len = 0; 879 } 880 // Free b_sst_array[] for buffer "buf". 881 // Used when syntax items changed to force resyncing everywhere. 882 void syn_stack_free_all(synblock_T *block) 883 { 884 syn_stack_free_block(block); 885 886 // When using "syntax" fold method, must update all folds. 887 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { 888 if (wp->w_s == block && foldmethodIsSyntax(wp)) { 889 foldUpdateAll(wp); 890 } 891 } 892 } 893 894 // Allocate the syntax state stack for syn_buf when needed. 895 // If the number of entries in b_sst_array[] is much too big or a bit too 896 // small, reallocate it. 897 // Also used to allocate b_sst_array[] for the first time. 898 static void syn_stack_alloc(void) 899 { 900 int len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2; 901 if (len < SST_MIN_ENTRIES) { 902 len = SST_MIN_ENTRIES; 903 } else if (len > SST_MAX_ENTRIES) { 904 len = SST_MAX_ENTRIES; 905 } 906 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len) { 907 // Allocate 50% too much, to avoid reallocating too often. 908 len = syn_buf->b_ml.ml_line_count; 909 len = (len + len / 2) / SST_DIST + Rows * 2; 910 if (len < SST_MIN_ENTRIES) { 911 len = SST_MIN_ENTRIES; 912 } else if (len > SST_MAX_ENTRIES) { 913 len = SST_MAX_ENTRIES; 914 } 915 916 if (syn_block->b_sst_array != NULL) { 917 // When shrinking the array, cleanup the existing stack. 918 // Make sure that all valid entries fit in the new array. 919 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len 920 && syn_stack_cleanup()) {} 921 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2) { 922 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2; 923 } 924 } 925 926 assert(len >= 0); 927 synstate_T *sstp = xcalloc((size_t)len, sizeof(synstate_T)); 928 929 synstate_T *to = sstp - 1; 930 if (syn_block->b_sst_array != NULL) { 931 // Move the states from the old array to the new one. 932 for (synstate_T *from = syn_block->b_sst_first; from != NULL; 933 from = from->sst_next) { 934 to++; 935 *to = *from; 936 to->sst_next = to + 1; 937 } 938 } 939 if (to != sstp - 1) { 940 to->sst_next = NULL; 941 syn_block->b_sst_first = sstp; 942 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1; 943 } else { 944 syn_block->b_sst_first = NULL; 945 syn_block->b_sst_freecount = len; 946 } 947 948 // Create the list of free entries. 949 syn_block->b_sst_firstfree = to + 1; 950 while (++to < sstp + len) { 951 to->sst_next = to + 1; 952 } 953 (sstp + len - 1)->sst_next = NULL; 954 955 xfree(syn_block->b_sst_array); 956 syn_block->b_sst_array = sstp; 957 syn_block->b_sst_len = len; 958 } 959 } 960 961 // Check for changes in a buffer to affect stored syntax states. Uses the 962 // b_mod_* fields. 963 // Called from update_screen(), before screen is being updated, once for each 964 // displayed buffer. 965 void syn_stack_apply_changes(buf_T *buf) 966 { 967 syn_stack_apply_changes_block(&buf->b_s, buf); 968 969 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { 970 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s)) { 971 syn_stack_apply_changes_block(wp->w_s, buf); 972 } 973 } 974 } 975 976 static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf) 977 { 978 synstate_T *prev = NULL; 979 for (synstate_T *p = block->b_sst_first; p != NULL;) { 980 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top) { 981 linenr_T n = p->sst_lnum + buf->b_mod_xlines; 982 if (n <= buf->b_mod_bot) { 983 // this state is inside the changed area, remove it 984 synstate_T *np = p->sst_next; 985 if (prev == NULL) { 986 block->b_sst_first = np; 987 } else { 988 prev->sst_next = np; 989 } 990 syn_stack_free_entry(block, p); 991 p = np; 992 continue; 993 } 994 // This state is below the changed area. Remember the line 995 // that needs to be parsed before this entry can be made valid 996 // again. 997 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top) { 998 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top) { 999 p->sst_change_lnum += buf->b_mod_xlines; 1000 } else { 1001 p->sst_change_lnum = buf->b_mod_top; 1002 } 1003 } 1004 if (p->sst_change_lnum == 0 1005 || p->sst_change_lnum < buf->b_mod_bot) { 1006 p->sst_change_lnum = buf->b_mod_bot; 1007 } 1008 1009 p->sst_lnum = n; 1010 } 1011 prev = p; 1012 p = p->sst_next; 1013 } 1014 } 1015 1016 /// Reduce the number of entries in the state stack for syn_buf. 1017 /// 1018 /// @return true if at least one entry was freed. 1019 static bool syn_stack_cleanup(void) 1020 { 1021 synstate_T *prev; 1022 disptick_T tick; 1023 int dist; 1024 bool retval = false; 1025 1026 if (syn_block->b_sst_first == NULL) { 1027 return retval; 1028 } 1029 1030 // Compute normal distance between non-displayed entries. 1031 if (syn_block->b_sst_len <= Rows) { 1032 dist = 999999; 1033 } else { 1034 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1; 1035 } 1036 1037 // Go through the list to find the "tick" for the oldest entry that can 1038 // be removed. Set "above" when the "tick" for the oldest entry is above 1039 // "b_sst_lasttick" (the display tick wraps around). 1040 tick = syn_block->b_sst_lasttick; 1041 bool above = false; 1042 prev = syn_block->b_sst_first; 1043 for (synstate_T *p = prev->sst_next; p != NULL; prev = p, p = p->sst_next) { 1044 if (prev->sst_lnum + dist > p->sst_lnum) { 1045 if (p->sst_tick > syn_block->b_sst_lasttick) { 1046 if (!above || p->sst_tick < tick) { 1047 tick = p->sst_tick; 1048 } 1049 above = true; 1050 } else if (!above && p->sst_tick < tick) { 1051 tick = p->sst_tick; 1052 } 1053 } 1054 } 1055 1056 // Go through the list to make the entries for the oldest tick at an 1057 // interval of several lines. 1058 prev = syn_block->b_sst_first; 1059 for (synstate_T *p = prev->sst_next; p != NULL; prev = p, p = p->sst_next) { 1060 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum) { 1061 // Move this entry from used list to free list 1062 prev->sst_next = p->sst_next; 1063 syn_stack_free_entry(syn_block, p); 1064 p = prev; 1065 retval = true; 1066 } 1067 } 1068 return retval; 1069 } 1070 1071 // Free the allocated memory for a syn_state item. 1072 // Move the entry into the free list. 1073 static void syn_stack_free_entry(synblock_T *block, synstate_T *p) 1074 { 1075 clear_syn_state(p); 1076 p->sst_next = block->b_sst_firstfree; 1077 block->b_sst_firstfree = p; 1078 block->b_sst_freecount++; 1079 } 1080 1081 // Find an entry in the list of state stacks at or before "lnum". 1082 // Returns NULL when there is no entry or the first entry is after "lnum". 1083 static synstate_T *syn_stack_find_entry(linenr_T lnum) 1084 { 1085 synstate_T *prev = NULL; 1086 for (synstate_T *p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next) { 1087 if (p->sst_lnum == lnum) { 1088 return p; 1089 } 1090 if (p->sst_lnum > lnum) { 1091 break; 1092 } 1093 } 1094 return prev; 1095 } 1096 1097 // Try saving the current state in b_sst_array[]. 1098 // The current state must be valid for the start of the current_lnum line! 1099 static synstate_T *store_current_state(void) 1100 { 1101 int i; 1102 synstate_T *p; 1103 bufstate_T *bp; 1104 stateitem_T *cur_si; 1105 synstate_T *sp = syn_stack_find_entry(current_lnum); 1106 1107 // If the current state contains a start or end pattern that continues 1108 // from the previous line, we can't use it. Don't store it then. 1109 for (i = current_state.ga_len - 1; i >= 0; i--) { 1110 cur_si = &CUR_STATE(i); 1111 if (cur_si->si_h_startpos.lnum >= current_lnum 1112 || cur_si->si_m_endpos.lnum >= current_lnum 1113 || cur_si->si_h_endpos.lnum >= current_lnum 1114 || (cur_si->si_end_idx 1115 && cur_si->si_eoe_pos.lnum >= current_lnum)) { 1116 break; 1117 } 1118 } 1119 if (i >= 0) { 1120 if (sp != NULL) { 1121 // find "sp" in the list and remove it 1122 if (syn_block->b_sst_first == sp) { 1123 // it's the first entry 1124 syn_block->b_sst_first = sp->sst_next; 1125 } else { 1126 // find the entry just before this one to adjust sst_next 1127 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next) { 1128 if (p->sst_next == sp) { 1129 break; 1130 } 1131 } 1132 if (p != NULL) { // just in case 1133 p->sst_next = sp->sst_next; 1134 } 1135 } 1136 syn_stack_free_entry(syn_block, sp); 1137 sp = NULL; 1138 } 1139 } else if (sp == NULL || sp->sst_lnum != current_lnum) { 1140 // Add a new entry 1141 // If no free items, cleanup the array first. 1142 if (syn_block->b_sst_freecount == 0) { 1143 syn_stack_cleanup(); 1144 // "sp" may have been moved to the freelist now 1145 sp = syn_stack_find_entry(current_lnum); 1146 } 1147 // Still no free items? Must be a strange problem... 1148 if (syn_block->b_sst_freecount == 0) { 1149 sp = NULL; 1150 } else { 1151 // Take the first item from the free list and put it in the used 1152 // list, after *sp 1153 p = syn_block->b_sst_firstfree; 1154 syn_block->b_sst_firstfree = p->sst_next; 1155 syn_block->b_sst_freecount--; 1156 if (sp == NULL) { 1157 // Insert in front of the list 1158 p->sst_next = syn_block->b_sst_first; 1159 syn_block->b_sst_first = p; 1160 } else { 1161 // insert in list after *sp 1162 p->sst_next = sp->sst_next; 1163 sp->sst_next = p; 1164 } 1165 sp = p; 1166 sp->sst_stacksize = 0; 1167 sp->sst_lnum = current_lnum; 1168 } 1169 } 1170 if (sp != NULL) { 1171 // When overwriting an existing state stack, clear it first 1172 clear_syn_state(sp); 1173 sp->sst_stacksize = current_state.ga_len; 1174 if (current_state.ga_len > SST_FIX_STATES) { 1175 // Need to clear it, might be something remaining from when the 1176 // length was less than SST_FIX_STATES. 1177 ga_init(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1); 1178 ga_grow(&sp->sst_union.sst_ga, current_state.ga_len); 1179 sp->sst_union.sst_ga.ga_len = current_state.ga_len; 1180 bp = SYN_STATE_P(&(sp->sst_union.sst_ga)); 1181 } else { 1182 bp = sp->sst_union.sst_stack; 1183 } 1184 for (i = 0; i < sp->sst_stacksize; i++) { 1185 bp[i].bs_idx = CUR_STATE(i).si_idx; 1186 bp[i].bs_flags = CUR_STATE(i).si_flags; 1187 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr; 1188 bp[i].bs_cchar = CUR_STATE(i).si_cchar; 1189 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch); 1190 } 1191 sp->sst_next_flags = current_next_flags; 1192 sp->sst_next_list = current_next_list; 1193 sp->sst_tick = display_tick; 1194 sp->sst_change_lnum = 0; 1195 } 1196 current_state_stored = true; 1197 return sp; 1198 } 1199 1200 // Copy a state stack from "from" in b_sst_array[] to current_state; 1201 static void load_current_state(synstate_T *from) 1202 { 1203 bufstate_T *bp; 1204 1205 clear_current_state(); 1206 validate_current_state(); 1207 keepend_level = -1; 1208 if (from->sst_stacksize) { 1209 ga_grow(¤t_state, from->sst_stacksize); 1210 if (from->sst_stacksize > SST_FIX_STATES) { 1211 bp = SYN_STATE_P(&(from->sst_union.sst_ga)); 1212 } else { 1213 bp = from->sst_union.sst_stack; 1214 } 1215 for (int i = 0; i < from->sst_stacksize; i++) { 1216 CUR_STATE(i).si_idx = bp[i].bs_idx; 1217 CUR_STATE(i).si_flags = bp[i].bs_flags; 1218 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr; 1219 CUR_STATE(i).si_cchar = bp[i].bs_cchar; 1220 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch); 1221 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND)) { 1222 keepend_level = i; 1223 } 1224 CUR_STATE(i).si_ends = false; 1225 CUR_STATE(i).si_m_lnum = 0; 1226 if (CUR_STATE(i).si_idx >= 0) { 1227 CUR_STATE(i).si_next_list = 1228 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list; 1229 } else { 1230 CUR_STATE(i).si_next_list = NULL; 1231 } 1232 update_si_attr(i); 1233 } 1234 current_state.ga_len = from->sst_stacksize; 1235 } 1236 current_next_list = from->sst_next_list; 1237 current_next_flags = from->sst_next_flags; 1238 current_lnum = from->sst_lnum; 1239 } 1240 1241 /// Compare saved state stack "*sp" with the current state. 1242 /// 1243 /// @return true when they are equal. 1244 static bool syn_stack_equal(synstate_T *sp) 1245 { 1246 bufstate_T *bp; 1247 1248 // First a quick check if the stacks have the same size end nextlist. 1249 if (sp->sst_stacksize != current_state.ga_len 1250 || sp->sst_next_list != current_next_list) { 1251 return false; 1252 } 1253 1254 // Need to compare all states on both stacks. 1255 if (sp->sst_stacksize > SST_FIX_STATES) { 1256 bp = SYN_STATE_P(&(sp->sst_union.sst_ga)); 1257 } else { 1258 bp = sp->sst_union.sst_stack; 1259 } 1260 1261 int i; 1262 for (i = current_state.ga_len; --i >= 0;) { 1263 // If the item has another index the state is different. 1264 if (bp[i].bs_idx != CUR_STATE(i).si_idx) { 1265 break; 1266 } 1267 if (bp[i].bs_extmatch == CUR_STATE(i).si_extmatch) { 1268 continue; 1269 } 1270 // When the extmatch pointers are different, the strings in them can 1271 // still be the same. Check if the extmatch references are equal. 1272 reg_extmatch_T *bsx = bp[i].bs_extmatch; 1273 reg_extmatch_T *six = CUR_STATE(i).si_extmatch; 1274 // If one of the extmatch pointers is NULL the states are different. 1275 if (bsx == NULL || six == NULL) { 1276 break; 1277 } 1278 int j; 1279 for (j = 0; j < NSUBEXP; j++) { 1280 // Check each referenced match string. They must all be equal. 1281 if (bsx->matches[j] != six->matches[j]) { 1282 // If the pointer is different it can still be the same text. 1283 // Compare the strings, ignore case when the start item has the 1284 // sp_ic flag set. 1285 if (bsx->matches[j] == NULL || six->matches[j] == NULL) { 1286 break; 1287 } 1288 if (mb_strcmp_ic((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic, 1289 (const char *)bsx->matches[j], 1290 (const char *)six->matches[j]) != 0) { 1291 break; 1292 } 1293 } 1294 } 1295 if (j != NSUBEXP) { 1296 break; 1297 } 1298 } 1299 return i < 0 ? true : false; 1300 } 1301 1302 // We stop parsing syntax above line "lnum". If the stored state at or below 1303 // this line depended on a change before it, it now depends on the line below 1304 // the last parsed line. 1305 // The window looks like this: 1306 // line which changed 1307 // displayed line 1308 // displayed line 1309 // lnum -> line below window 1310 void syntax_end_parsing(win_T *wp, linenr_T lnum) 1311 { 1312 synstate_T *sp; 1313 1314 if (syn_block != wp->w_s) { 1315 return; // not the right window 1316 } 1317 sp = syn_stack_find_entry(lnum); 1318 if (sp != NULL && sp->sst_lnum < lnum) { 1319 sp = sp->sst_next; 1320 } 1321 1322 if (sp != NULL && sp->sst_change_lnum != 0) { 1323 sp->sst_change_lnum = lnum; 1324 } 1325 } 1326 1327 // End of handling of the state stack. 1328 // ************************************** 1329 1330 static void invalidate_current_state(void) 1331 { 1332 clear_current_state(); 1333 current_state.ga_itemsize = 0; // mark current_state invalid 1334 current_next_list = NULL; 1335 keepend_level = -1; 1336 } 1337 1338 static void validate_current_state(void) 1339 { 1340 current_state.ga_itemsize = sizeof(stateitem_T); 1341 ga_set_growsize(¤t_state, 3); 1342 } 1343 1344 /// This will only be called just after get_syntax_attr() for the previous 1345 /// line, to check if the next line needs to be redrawn too. 1346 /// 1347 /// @return true if the syntax at start of lnum changed since last time. 1348 bool syntax_check_changed(linenr_T lnum) 1349 { 1350 bool retval = true; 1351 synstate_T *sp; 1352 1353 // Check the state stack when: 1354 // - lnum is just below the previously syntaxed line. 1355 // - lnum is not before the lines with saved states. 1356 // - lnum is not past the lines with saved states. 1357 // - lnum is at or before the last changed line. 1358 if (VALID_STATE(¤t_state) && lnum == current_lnum + 1) { 1359 sp = syn_stack_find_entry(lnum); 1360 if (sp != NULL && sp->sst_lnum == lnum) { 1361 // finish the previous line (needed when not all of the line was 1362 // drawn) 1363 syn_finish_line(false); 1364 1365 // Compare the current state with the previously saved state of 1366 // the line. 1367 if (syn_stack_equal(sp)) { 1368 retval = false; 1369 } 1370 1371 // Store the current state in b_sst_array[] for later use. 1372 current_lnum++; 1373 store_current_state(); 1374 } 1375 } 1376 1377 return retval; 1378 } 1379 1380 /// Finish the current line. 1381 /// This doesn't return any attributes, it only gets the state at the end of 1382 /// the line. It can start anywhere in the line, as long as the current state 1383 /// is valid. 1384 /// 1385 /// @param syncing called for syncing 1386 static bool syn_finish_line(const bool syncing) 1387 { 1388 while (!current_finished) { 1389 syn_current_attr(syncing, false, NULL, false); 1390 1391 // When syncing, and found some item, need to check the item. 1392 if (syncing && current_state.ga_len) { 1393 // Check for match with sync item. 1394 const stateitem_T *const cur_si = &CUR_STATE(current_state.ga_len - 1); 1395 if (cur_si->si_idx >= 0 1396 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags 1397 & (HL_SYNC_HERE|HL_SYNC_THERE))) { 1398 return true; 1399 } 1400 1401 // syn_current_attr() will have skipped the check for an item 1402 // that ends here, need to do that now. Be careful not to go 1403 // past the NUL. 1404 const colnr_T prev_current_col = current_col; 1405 if (syn_getcurline()[current_col] != NUL) { 1406 current_col++; 1407 } 1408 check_state_ends(); 1409 current_col = prev_current_col; 1410 } 1411 current_col++; 1412 } 1413 return false; 1414 } 1415 1416 /// Gets highlight attributes for next character. 1417 /// Must first call syntax_start() once for the line. 1418 /// "col" is normally 0 for the first use in a line, and increments by one each 1419 /// time. It's allowed to skip characters and to stop before the end of the 1420 /// line. But only a "col" after a previously used column is allowed. 1421 /// When "can_spell" is not NULL set it to true when spell-checking should be 1422 /// done. 1423 /// 1424 /// @param keep_state keep state of char at "col" 1425 /// 1426 /// @return highlight attributes for next character. 1427 int get_syntax_attr(const colnr_T col, bool *const can_spell, const bool keep_state) 1428 { 1429 int attr = 0; 1430 1431 if (can_spell != NULL) { 1432 // Default: Only do spelling when there is no @Spell cluster or when 1433 // ":syn spell toplevel" was used. 1434 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT 1435 ? (syn_block->b_spell_cluster_id == 0) 1436 : (syn_block->b_syn_spell == SYNSPL_TOP); 1437 } 1438 1439 // check for out of memory situation 1440 if (syn_block->b_sst_array == NULL) { 1441 return 0; 1442 } 1443 1444 // After 'synmaxcol' the attribute is always zero. 1445 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc) { 1446 clear_current_state(); 1447 current_id = 0; 1448 current_trans_id = 0; 1449 current_flags = 0; 1450 current_seqnr = 0; 1451 return 0; 1452 } 1453 1454 // Make sure current_state is valid 1455 if (INVALID_STATE(¤t_state)) { 1456 validate_current_state(); 1457 } 1458 1459 // Skip from the current column to "col", get the attributes for "col". 1460 while (current_col <= col) { 1461 attr = syn_current_attr(false, true, can_spell, 1462 current_col == col ? keep_state : false); 1463 current_col++; 1464 } 1465 1466 return attr; 1467 } 1468 1469 /// Get syntax attributes for current_lnum, current_col. 1470 /// 1471 /// @param syncing When true: called for syncing 1472 /// @param displaying result will be displayed 1473 /// @param can_spell return: do spell checking 1474 /// @param keep_state keep syntax stack afterwards 1475 static int syn_current_attr(const bool syncing, const bool displaying, bool *const can_spell, 1476 const bool keep_state) 1477 { 1478 lpos_T endpos; 1479 lpos_T hl_startpos; 1480 lpos_T hl_endpos; 1481 lpos_T eos_pos; // end-of-start match (start region) 1482 lpos_T eoe_pos; // end-of-end pattern 1483 int end_idx; // group ID for end pattern 1484 stateitem_T *cur_si; 1485 stateitem_T *sip = NULL; 1486 int startcol; 1487 int endcol; 1488 int flags; 1489 int cchar; 1490 int16_t *next_list; 1491 bool found_match; // found usable match 1492 static bool try_next_column = false; // must try in next col 1493 regmmatch_T regmatch; 1494 lpos_T pos; 1495 reg_extmatch_T *cur_extmatch = NULL; 1496 char buf_chartab[32]; // chartab array for syn iskeyword 1497 char *line; // current line. NOTE: becomes invalid after 1498 // looking for a pattern match! 1499 1500 // variables for zero-width matches that have a "nextgroup" argument 1501 bool keep_next_list; 1502 bool zero_width_next_list = false; 1503 garray_T zero_width_next_ga; 1504 1505 // No character, no attributes! Past end of line? 1506 // Do try matching with an empty line (could be the start of a region). 1507 line = syn_getcurline(); 1508 if (line[current_col] == NUL && current_col != 0) { 1509 // If we found a match after the last column, use it. 1510 if (next_match_idx >= 0 && next_match_col >= (int)current_col 1511 && next_match_col != MAXCOL) { 1512 push_next_match(); 1513 } 1514 1515 current_finished = true; 1516 current_state_stored = false; 1517 return 0; 1518 } 1519 1520 // if the current or next character is NUL, we will finish the line now 1521 if (line[current_col] == NUL || line[current_col + 1] == NUL) { 1522 current_finished = true; 1523 current_state_stored = false; 1524 } 1525 1526 // When in the previous column there was a match but it could not be used 1527 // (empty match or already matched in this column) need to try again in 1528 // the next column. 1529 if (try_next_column) { 1530 next_match_idx = -1; 1531 try_next_column = false; 1532 } 1533 1534 // Only check for keywords when not syncing and there are some. 1535 const bool do_keywords = !syncing 1536 && (syn_block->b_keywtab.ht_used > 0 1537 || syn_block->b_keywtab_ic.ht_used > 0); 1538 1539 // Init the list of zero-width matches with a nextlist. This is used to 1540 // avoid matching the same item in the same position twice. 1541 ga_init(&zero_width_next_ga, (int)sizeof(int), 10); 1542 1543 // use syntax iskeyword option 1544 save_chartab(buf_chartab); 1545 1546 // Repeat matching keywords and patterns, to find contained items at the 1547 // same column. This stops when there are no extra matches at the current 1548 // column. 1549 do { 1550 found_match = false; 1551 keep_next_list = false; 1552 int syn_id = 0; 1553 1554 // 1. Check for a current state. 1555 // Only when there is no current state, or if the current state may 1556 // contain other things, we need to check for keywords and patterns. 1557 // Always need to check for contained items if some item has the 1558 // "containedin" argument (takes extra time!). 1559 if (current_state.ga_len) { 1560 cur_si = &CUR_STATE(current_state.ga_len - 1); 1561 } else { 1562 cur_si = NULL; 1563 } 1564 1565 if (syn_block->b_syn_containedin || cur_si == NULL 1566 || cur_si->si_cont_list != NULL) { 1567 // 2. Check for keywords, if on a keyword char after a non-keyword 1568 // char. Don't do this when syncing. 1569 if (do_keywords) { 1570 line = syn_getcurline(); 1571 const char *cur_pos = line + current_col; 1572 if (vim_iswordp_buf(cur_pos, syn_buf) 1573 && (current_col == 0 1574 || !vim_iswordp_buf(cur_pos - 1 - 1575 utf_head_off(line, cur_pos - 1), 1576 syn_buf))) { 1577 syn_id = check_keyword_id(line, (int)current_col, &endcol, &flags, 1578 &next_list, cur_si, &cchar); 1579 if (syn_id != 0) { 1580 push_current_state(KEYWORD_IDX); 1581 { 1582 cur_si = &CUR_STATE(current_state.ga_len - 1); 1583 cur_si->si_m_startcol = current_col; 1584 cur_si->si_h_startpos.lnum = current_lnum; 1585 cur_si->si_h_startpos.col = 0; // starts right away 1586 cur_si->si_m_endpos.lnum = current_lnum; 1587 cur_si->si_m_endpos.col = endcol; 1588 cur_si->si_h_endpos.lnum = current_lnum; 1589 cur_si->si_h_endpos.col = endcol; 1590 cur_si->si_ends = true; 1591 cur_si->si_end_idx = 0; 1592 cur_si->si_flags = flags; 1593 cur_si->si_seqnr = next_seqnr++; 1594 cur_si->si_cchar = cchar; 1595 if (current_state.ga_len > 1) { 1596 cur_si->si_flags |= 1597 CUR_STATE(current_state.ga_len - 2).si_flags 1598 & HL_CONCEAL; 1599 } 1600 cur_si->si_id = syn_id; 1601 cur_si->si_trans_id = syn_id; 1602 if (flags & HL_TRANSP) { 1603 if (current_state.ga_len < 2) { 1604 cur_si->si_attr = 0; 1605 cur_si->si_trans_id = 0; 1606 } else { 1607 cur_si->si_attr = CUR_STATE(current_state.ga_len - 2).si_attr; 1608 cur_si->si_trans_id = CUR_STATE(current_state.ga_len - 2).si_trans_id; 1609 } 1610 } else { 1611 cur_si->si_attr = syn_id2attr(syn_id); 1612 } 1613 cur_si->si_cont_list = NULL; 1614 cur_si->si_next_list = next_list; 1615 check_keepend(); 1616 } 1617 } 1618 } 1619 } 1620 1621 // 3. Check for patterns (only if no keyword found). 1622 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len) { 1623 // If we didn't check for a match yet, or we are past it, check 1624 // for any match with a pattern. 1625 if (next_match_idx < 0 || next_match_col < (int)current_col) { 1626 // Check all relevant patterns for a match at this 1627 // position. This is complicated, because matching with a 1628 // pattern takes quite a bit of time, thus we want to 1629 // avoid doing it when it's not needed. 1630 next_match_idx = 0; // no match in this line yet 1631 next_match_col = MAXCOL; 1632 for (int idx = syn_block->b_syn_patterns.ga_len; --idx >= 0;) { 1633 synpat_T *const spp = &(SYN_ITEMS(syn_block)[idx]); 1634 if (spp->sp_syncing == syncing 1635 && (displaying || !(spp->sp_flags & HL_DISPLAY)) 1636 && (spp->sp_type == SPTYPE_MATCH 1637 || spp->sp_type == SPTYPE_START) 1638 && (current_next_list != NULL 1639 ? in_id_list(NULL, current_next_list, &spp->sp_syn, 0) 1640 : (cur_si == NULL 1641 ? !(spp->sp_flags & HL_CONTAINED) 1642 : in_id_list(cur_si, 1643 cur_si->si_cont_list, &spp->sp_syn, 1644 spp->sp_flags)))) { 1645 // If we already tried matching in this line, and 1646 // there isn't a match before next_match_col, skip 1647 // this item. 1648 if (spp->sp_line_id == current_line_id 1649 && spp->sp_startcol >= next_match_col) { 1650 continue; 1651 } 1652 spp->sp_line_id = current_line_id; 1653 1654 colnr_T lc_col = current_col - spp->sp_offsets[SPO_LC_OFF]; 1655 if (lc_col < 0) { 1656 lc_col = 0; 1657 } 1658 1659 regmatch.rmm_ic = spp->sp_ic; 1660 regmatch.regprog = spp->sp_prog; 1661 int r = syn_regexec(®match, current_lnum, lc_col, 1662 IF_SYN_TIME(&spp->sp_time)); 1663 spp->sp_prog = regmatch.regprog; 1664 if (!r) { 1665 // no match in this line, try another one 1666 spp->sp_startcol = MAXCOL; 1667 continue; 1668 } 1669 1670 // Compute the first column of the match. 1671 syn_add_start_off(&pos, ®match, 1672 spp, SPO_MS_OFF, -1); 1673 if (pos.lnum > current_lnum) { 1674 // must have used end of match in a next line, 1675 // we can't handle that 1676 spp->sp_startcol = MAXCOL; 1677 continue; 1678 } 1679 startcol = pos.col; 1680 1681 // remember the next column where this pattern 1682 // matches in the current line 1683 spp->sp_startcol = startcol; 1684 1685 // If a previously found match starts at a lower 1686 // column number, don't use this one. 1687 if (startcol >= next_match_col) { 1688 continue; 1689 } 1690 1691 // If we matched this pattern at this position 1692 // before, skip it. Must retry in the next 1693 // column, because it may match from there. 1694 if (did_match_already(idx, &zero_width_next_ga)) { 1695 try_next_column = true; 1696 continue; 1697 } 1698 1699 endpos.lnum = regmatch.endpos[0].lnum; 1700 endpos.col = regmatch.endpos[0].col; 1701 1702 // Compute the highlight start. 1703 syn_add_start_off(&hl_startpos, ®match, 1704 spp, SPO_HS_OFF, -1); 1705 1706 // Compute the region start. 1707 // Default is to use the end of the match. 1708 syn_add_end_off(&eos_pos, ®match, 1709 spp, SPO_RS_OFF, 0); 1710 1711 // Grab the external submatches before they get 1712 // overwritten. Reference count doesn't change. 1713 unref_extmatch(cur_extmatch); 1714 cur_extmatch = re_extmatch_out; 1715 re_extmatch_out = NULL; 1716 1717 flags = 0; 1718 eoe_pos.lnum = 0; // avoid warning 1719 eoe_pos.col = 0; 1720 end_idx = 0; 1721 hl_endpos.lnum = 0; 1722 1723 // For a "oneline" the end must be found in the 1724 // same line too. Search for it after the end of 1725 // the match with the start pattern. Set the 1726 // resulting end positions at the same time. 1727 if (spp->sp_type == SPTYPE_START 1728 && (spp->sp_flags & HL_ONELINE)) { 1729 lpos_T startpos; 1730 1731 startpos = endpos; 1732 find_endpos(idx, &startpos, &endpos, &hl_endpos, 1733 &flags, &eoe_pos, &end_idx, cur_extmatch); 1734 if (endpos.lnum == 0) { 1735 continue; // not found 1736 } 1737 } else if (spp->sp_type == SPTYPE_MATCH) { 1738 // For a "match" the size must be > 0 after the 1739 // end offset needs has been added. Except when 1740 // syncing. 1741 syn_add_end_off(&hl_endpos, ®match, spp, 1742 SPO_HE_OFF, 0); 1743 syn_add_end_off(&endpos, ®match, spp, 1744 SPO_ME_OFF, 0); 1745 if (endpos.lnum == current_lnum 1746 && (int)endpos.col + syncing < startcol) { 1747 // If an empty string is matched, may need 1748 // to try matching again at next column. 1749 if (regmatch.startpos[0].col == regmatch.endpos[0].col) { 1750 try_next_column = true; 1751 } 1752 continue; 1753 } 1754 } 1755 1756 // keep the best match so far in next_match_* 1757 1758 // Highlighting must start after startpos and end 1759 // before endpos. 1760 if (hl_startpos.lnum == current_lnum 1761 && (int)hl_startpos.col < startcol) { 1762 hl_startpos.col = startcol; 1763 } 1764 limit_pos_zero(&hl_endpos, &endpos); 1765 1766 next_match_idx = idx; 1767 next_match_col = startcol; 1768 next_match_m_endpos = endpos; 1769 next_match_h_endpos = hl_endpos; 1770 next_match_h_startpos = hl_startpos; 1771 next_match_flags = flags; 1772 next_match_eos_pos = eos_pos; 1773 next_match_eoe_pos = eoe_pos; 1774 next_match_end_idx = end_idx; 1775 unref_extmatch(next_match_extmatch); 1776 next_match_extmatch = cur_extmatch; 1777 cur_extmatch = NULL; 1778 } 1779 } 1780 } 1781 1782 // If we found a match at the current column, use it. 1783 if (next_match_idx >= 0 && next_match_col == (int)current_col) { 1784 synpat_T *lspp; 1785 1786 // When a zero-width item matched which has a nextgroup, 1787 // don't push the item but set nextgroup. 1788 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]); 1789 if (next_match_m_endpos.lnum == current_lnum 1790 && next_match_m_endpos.col == current_col 1791 && lspp->sp_next_list != NULL) { 1792 current_next_list = lspp->sp_next_list; 1793 current_next_flags = lspp->sp_flags; 1794 keep_next_list = true; 1795 zero_width_next_list = true; 1796 1797 // Add the index to a list, so that we can check 1798 // later that we don't match it again (and cause an 1799 // endless loop). 1800 GA_APPEND(int, &zero_width_next_ga, next_match_idx); 1801 next_match_idx = -1; 1802 } else { 1803 cur_si = push_next_match(); 1804 } 1805 found_match = true; 1806 } 1807 } 1808 } 1809 1810 // Handle searching for nextgroup match. 1811 if (current_next_list != NULL && !keep_next_list) { 1812 // If a nextgroup was not found, continue looking for one if: 1813 // - this is an empty line and the "skipempty" option was given 1814 // - we are on white space and the "skipwhite" option was given 1815 if (!found_match) { 1816 line = syn_getcurline(); 1817 if (((current_next_flags & HL_SKIPWHITE) 1818 && ascii_iswhite(line[current_col])) 1819 || ((current_next_flags & HL_SKIPEMPTY) 1820 && *line == NUL)) { 1821 break; 1822 } 1823 } 1824 1825 // If a nextgroup was found: Use it, and continue looking for 1826 // contained matches. 1827 // If a nextgroup was not found: Continue looking for a normal 1828 // match. 1829 // When did set current_next_list for a zero-width item and no 1830 // match was found don't loop (would get stuck). 1831 current_next_list = NULL; 1832 next_match_idx = -1; 1833 if (!zero_width_next_list) { 1834 found_match = true; 1835 } 1836 } 1837 } while (found_match); 1838 1839 restore_chartab(buf_chartab); 1840 1841 // Use attributes from the current state, if within its highlighting. 1842 // If not, use attributes from the current-but-one state, etc. 1843 current_attr = 0; 1844 current_id = 0; 1845 current_trans_id = 0; 1846 current_flags = 0; 1847 current_seqnr = 0; 1848 if (cur_si != NULL) { 1849 for (int idx = current_state.ga_len - 1; idx >= 0; idx--) { 1850 sip = &CUR_STATE(idx); 1851 if ((current_lnum > sip->si_h_startpos.lnum 1852 || (current_lnum == sip->si_h_startpos.lnum 1853 && current_col >= sip->si_h_startpos.col)) 1854 && (sip->si_h_endpos.lnum == 0 1855 || current_lnum < sip->si_h_endpos.lnum 1856 || (current_lnum == sip->si_h_endpos.lnum 1857 && current_col < sip->si_h_endpos.col))) { 1858 current_attr = sip->si_attr; 1859 current_id = sip->si_id; 1860 current_trans_id = sip->si_trans_id; 1861 current_flags = sip->si_flags; 1862 current_seqnr = sip->si_seqnr; 1863 current_sub_char = sip->si_cchar; 1864 break; 1865 } 1866 } 1867 1868 if (can_spell != NULL) { 1869 struct sp_syn sps; 1870 1871 // set "can_spell" to true if spell checking is supposed to be 1872 // done in the current item. 1873 if (syn_block->b_spell_cluster_id == 0) { 1874 // There is no @Spell cluster: Do spelling for items without 1875 // @NoSpell cluster. 1876 if (syn_block->b_nospell_cluster_id == 0 1877 || current_trans_id == 0) { 1878 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP); 1879 } else { 1880 sps.inc_tag = 0; 1881 sps.id = (int16_t)syn_block->b_nospell_cluster_id; 1882 sps.cont_in_list = NULL; 1883 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0); 1884 } 1885 } else { 1886 // The @Spell cluster is defined: Do spelling in items with 1887 // the @Spell cluster. But not when @NoSpell is also there. 1888 // At the toplevel only spell check when ":syn spell toplevel" 1889 // was used. 1890 if (current_trans_id == 0) { 1891 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP); 1892 } else { 1893 sps.inc_tag = 0; 1894 sps.id = (int16_t)syn_block->b_spell_cluster_id; 1895 sps.cont_in_list = NULL; 1896 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0); 1897 1898 if (syn_block->b_nospell_cluster_id != 0) { 1899 sps.id = (int16_t)syn_block->b_nospell_cluster_id; 1900 if (in_id_list(sip, sip->si_cont_list, &sps, 0)) { 1901 *can_spell = false; 1902 } 1903 } 1904 } 1905 } 1906 } 1907 1908 // Check for end of current state (and the states before it) at the 1909 // next column. Don't do this for syncing, because we would miss a 1910 // single character match. 1911 // First check if the current state ends at the current column. It 1912 // may be for an empty match and a containing item might end in the 1913 // current column. 1914 if (!syncing && !keep_state) { 1915 check_state_ends(); 1916 if (!GA_EMPTY(¤t_state) 1917 && syn_getcurline()[current_col] != NUL) { 1918 current_col++; 1919 check_state_ends(); 1920 current_col--; 1921 } 1922 } 1923 } else if (can_spell != NULL) { 1924 // Default: Only do spelling when there is no @Spell cluster or when 1925 // ":syn spell toplevel" was used. 1926 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT 1927 ? (syn_block->b_spell_cluster_id == 0) 1928 : (syn_block->b_syn_spell == SYNSPL_TOP); 1929 } 1930 1931 // nextgroup ends at end of line, unless "skipnl" or "skipempty" present 1932 if (current_next_list != NULL 1933 && (line = syn_getcurline())[current_col] != NUL 1934 && line[current_col + 1] == NUL 1935 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))) { 1936 current_next_list = NULL; 1937 } 1938 1939 if (!GA_EMPTY(&zero_width_next_ga)) { 1940 ga_clear(&zero_width_next_ga); 1941 } 1942 1943 // No longer need external matches. But keep next_match_extmatch. 1944 unref_extmatch(re_extmatch_out); 1945 re_extmatch_out = NULL; 1946 unref_extmatch(cur_extmatch); 1947 1948 return current_attr; 1949 } 1950 1951 /// @return true if we already matched pattern "idx" at the current column. 1952 static bool did_match_already(int idx, garray_T *gap) 1953 { 1954 for (int i = current_state.ga_len; --i >= 0;) { 1955 if (CUR_STATE(i).si_m_startcol == (int)current_col 1956 && CUR_STATE(i).si_m_lnum == (int)current_lnum 1957 && CUR_STATE(i).si_idx == idx) { 1958 return true; 1959 } 1960 } 1961 1962 // Zero-width matches with a nextgroup argument are not put on the syntax 1963 // stack, and can only be matched once anyway. 1964 for (int i = gap->ga_len; --i >= 0;) { 1965 if (((int *)(gap->ga_data))[i] == idx) { 1966 return true; 1967 } 1968 } 1969 1970 return false; 1971 } 1972 1973 // Push the next match onto the stack. 1974 static stateitem_T *push_next_match(void) 1975 { 1976 stateitem_T *cur_si; 1977 synpat_T *spp; 1978 int save_flags; 1979 1980 spp = &(SYN_ITEMS(syn_block)[next_match_idx]); 1981 1982 // Push the item in current_state stack; 1983 push_current_state(next_match_idx); 1984 { 1985 // If it's a start-skip-end type that crosses lines, figure out how 1986 // much it continues in this line. Otherwise just fill in the length. 1987 cur_si = &CUR_STATE(current_state.ga_len - 1); 1988 cur_si->si_h_startpos = next_match_h_startpos; 1989 cur_si->si_m_startcol = current_col; 1990 cur_si->si_m_lnum = current_lnum; 1991 cur_si->si_flags = spp->sp_flags; 1992 cur_si->si_seqnr = next_seqnr++; 1993 cur_si->si_cchar = spp->sp_cchar; 1994 if (current_state.ga_len > 1) { 1995 cur_si->si_flags |= 1996 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL; 1997 } 1998 cur_si->si_next_list = spp->sp_next_list; 1999 cur_si->si_extmatch = ref_extmatch(next_match_extmatch); 2000 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE)) { 2001 // Try to find the end pattern in the current line 2002 update_si_end(cur_si, (int)(next_match_m_endpos.col), true); 2003 check_keepend(); 2004 } else { 2005 cur_si->si_m_endpos = next_match_m_endpos; 2006 cur_si->si_h_endpos = next_match_h_endpos; 2007 cur_si->si_ends = true; 2008 cur_si->si_flags |= next_match_flags; 2009 cur_si->si_eoe_pos = next_match_eoe_pos; 2010 cur_si->si_end_idx = next_match_end_idx; 2011 } 2012 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND)) { 2013 keepend_level = current_state.ga_len - 1; 2014 } 2015 check_keepend(); 2016 update_si_attr(current_state.ga_len - 1); 2017 2018 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS); 2019 // If the start pattern has another highlight group, push another item 2020 // on the stack for the start pattern. 2021 if (spp->sp_type == SPTYPE_START && spp->sp_syn_match_id != 0) { 2022 push_current_state(next_match_idx); 2023 cur_si = &CUR_STATE(current_state.ga_len - 1); 2024 cur_si->si_h_startpos = next_match_h_startpos; 2025 cur_si->si_m_startcol = current_col; 2026 cur_si->si_m_lnum = current_lnum; 2027 cur_si->si_m_endpos = next_match_eos_pos; 2028 cur_si->si_h_endpos = next_match_eos_pos; 2029 cur_si->si_ends = true; 2030 cur_si->si_end_idx = 0; 2031 cur_si->si_flags = HL_MATCH; 2032 cur_si->si_seqnr = next_seqnr++; 2033 cur_si->si_flags |= save_flags; 2034 if (cur_si->si_flags & HL_CONCEALENDS) { 2035 cur_si->si_flags |= HL_CONCEAL; 2036 } 2037 cur_si->si_next_list = NULL; 2038 check_keepend(); 2039 update_si_attr(current_state.ga_len - 1); 2040 } 2041 } 2042 2043 next_match_idx = -1; // try other match next time 2044 2045 return cur_si; 2046 } 2047 2048 // Check for end of current state (and the states before it). 2049 static void check_state_ends(void) 2050 { 2051 stateitem_T *cur_si; 2052 int had_extend; 2053 2054 cur_si = &CUR_STATE(current_state.ga_len - 1); 2055 while (true) { 2056 if (cur_si->si_ends 2057 && (cur_si->si_m_endpos.lnum < current_lnum 2058 || (cur_si->si_m_endpos.lnum == current_lnum 2059 && cur_si->si_m_endpos.col <= current_col))) { 2060 // If there is an end pattern group ID, highlight the end pattern 2061 // now. No need to pop the current item from the stack. 2062 // Only do this if the end pattern continues beyond the current 2063 // position. 2064 if (cur_si->si_end_idx 2065 && (cur_si->si_eoe_pos.lnum > current_lnum 2066 || (cur_si->si_eoe_pos.lnum == current_lnum 2067 && cur_si->si_eoe_pos.col > current_col))) { 2068 cur_si->si_idx = cur_si->si_end_idx; 2069 cur_si->si_end_idx = 0; 2070 cur_si->si_m_endpos = cur_si->si_eoe_pos; 2071 cur_si->si_h_endpos = cur_si->si_eoe_pos; 2072 cur_si->si_flags |= HL_MATCH; 2073 cur_si->si_seqnr = next_seqnr++; 2074 if (cur_si->si_flags & HL_CONCEALENDS) { 2075 cur_si->si_flags |= HL_CONCEAL; 2076 } 2077 update_si_attr(current_state.ga_len - 1); 2078 2079 // nextgroup= should not match in the end pattern 2080 current_next_list = NULL; 2081 2082 // what matches next may be different now, clear it 2083 next_match_idx = 0; 2084 next_match_col = MAXCOL; 2085 break; 2086 } 2087 2088 // handle next_list, unless at end of line and no "skipnl" or 2089 // "skipempty" 2090 current_next_list = cur_si->si_next_list; 2091 current_next_flags = cur_si->si_flags; 2092 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)) 2093 && syn_getcurline()[current_col] == NUL) { 2094 current_next_list = NULL; 2095 } 2096 2097 // When the ended item has "extend", another item with 2098 // "keepend" now needs to check for its end. 2099 had_extend = (cur_si->si_flags & HL_EXTEND); 2100 2101 pop_current_state(); 2102 2103 if (GA_EMPTY(¤t_state)) { 2104 break; 2105 } 2106 2107 if (had_extend && keepend_level >= 0) { 2108 syn_update_ends(false); 2109 if (GA_EMPTY(¤t_state)) { 2110 break; 2111 } 2112 } 2113 2114 cur_si = &CUR_STATE(current_state.ga_len - 1); 2115 2116 // Only for a region the search for the end continues after 2117 // the end of the contained item. If the contained match 2118 // included the end-of-line, break here, the region continues. 2119 // Don't do this when: 2120 // - "keepend" is used for the contained item 2121 // - not at the end of the line (could be end="x$"me=e-1). 2122 // - "excludenl" is used (HL_HAS_EOL won't be set) 2123 if (cur_si->si_idx >= 0 2124 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type == SPTYPE_START 2125 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND))) { 2126 update_si_end(cur_si, (int)current_col, true); 2127 check_keepend(); 2128 if ((current_next_flags & HL_HAS_EOL) 2129 && keepend_level < 0 2130 && syn_getcurline()[current_col] == NUL) { 2131 break; 2132 } 2133 } 2134 } else { 2135 break; 2136 } 2137 } 2138 } 2139 2140 // Update an entry in the current_state stack for a match or region. This 2141 // fills in si_attr, si_next_list and si_cont_list. 2142 static void update_si_attr(int idx) 2143 { 2144 stateitem_T *sip = &CUR_STATE(idx); 2145 synpat_T *spp; 2146 2147 // This should not happen... 2148 if (sip->si_idx < 0) { 2149 return; 2150 } 2151 2152 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]); 2153 if (sip->si_flags & HL_MATCH) { 2154 sip->si_id = spp->sp_syn_match_id; 2155 } else { 2156 sip->si_id = spp->sp_syn.id; 2157 } 2158 sip->si_attr = syn_id2attr(sip->si_id); 2159 sip->si_trans_id = sip->si_id; 2160 if (sip->si_flags & HL_MATCH) { 2161 sip->si_cont_list = NULL; 2162 } else { 2163 sip->si_cont_list = spp->sp_cont_list; 2164 } 2165 2166 // For transparent items, take attr from outer item. 2167 // Also take cont_list, if there is none. 2168 // Don't do this for the matchgroup of a start or end pattern. 2169 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH)) { 2170 if (idx == 0) { 2171 sip->si_attr = 0; 2172 sip->si_trans_id = 0; 2173 if (sip->si_cont_list == NULL) { 2174 sip->si_cont_list = ID_LIST_ALL; 2175 } 2176 } else { 2177 sip->si_attr = CUR_STATE(idx - 1).si_attr; 2178 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id; 2179 if (sip->si_cont_list == NULL) { 2180 sip->si_flags |= HL_TRANS_CONT; 2181 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list; 2182 } 2183 } 2184 } 2185 } 2186 2187 // Check the current stack for patterns with "keepend" flag. 2188 // Propagate the match-end to contained items, until a "skipend" item is found. 2189 static void check_keepend(void) 2190 { 2191 int i; 2192 lpos_T maxpos; 2193 lpos_T maxpos_h; 2194 stateitem_T *sip; 2195 2196 // This check can consume a lot of time; only do it from the level where 2197 // there really is a keepend. 2198 if (keepend_level < 0) { 2199 return; 2200 } 2201 2202 // Find the last index of an "extend" item. "keepend" items before that 2203 // won't do anything. If there is no "extend" item "i" will be 2204 // "keepend_level" and all "keepend" items will work normally. 2205 for (i = current_state.ga_len - 1; i > keepend_level; i--) { 2206 if (CUR_STATE(i).si_flags & HL_EXTEND) { 2207 break; 2208 } 2209 } 2210 2211 maxpos.lnum = 0; 2212 maxpos.col = 0; 2213 maxpos_h.lnum = 0; 2214 maxpos_h.col = 0; 2215 for (; i < current_state.ga_len; i++) { 2216 sip = &CUR_STATE(i); 2217 if (maxpos.lnum != 0) { 2218 limit_pos_zero(&sip->si_m_endpos, &maxpos); 2219 limit_pos_zero(&sip->si_h_endpos, &maxpos_h); 2220 limit_pos_zero(&sip->si_eoe_pos, &maxpos); 2221 sip->si_ends = true; 2222 } 2223 if (sip->si_ends && (sip->si_flags & HL_KEEPEND)) { 2224 if (maxpos.lnum == 0 2225 || maxpos.lnum > sip->si_m_endpos.lnum 2226 || (maxpos.lnum == sip->si_m_endpos.lnum 2227 && maxpos.col > sip->si_m_endpos.col)) { 2228 maxpos = sip->si_m_endpos; 2229 } 2230 if (maxpos_h.lnum == 0 2231 || maxpos_h.lnum > sip->si_h_endpos.lnum 2232 || (maxpos_h.lnum == sip->si_h_endpos.lnum 2233 && maxpos_h.col > sip->si_h_endpos.col)) { 2234 maxpos_h = sip->si_h_endpos; 2235 } 2236 } 2237 } 2238 } 2239 2240 /// Update an entry in the current_state stack for a start-skip-end pattern. 2241 /// This finds the end of the current item, if it's in the current line. 2242 /// 2243 /// @param startcol where to start searching for the end 2244 /// @param force when true overrule a previous end 2245 /// 2246 /// @return the flags for the matched END. 2247 static void update_si_end(stateitem_T *sip, int startcol, bool force) 2248 { 2249 lpos_T hl_endpos; 2250 lpos_T end_endpos; 2251 2252 // return quickly for a keyword 2253 if (sip->si_idx < 0) { 2254 return; 2255 } 2256 2257 // Don't update when it's already done. Can be a match of an end pattern 2258 // that started in a previous line. Watch out: can also be a "keepend" 2259 // from a containing item. 2260 if (!force && sip->si_m_endpos.lnum >= current_lnum) { 2261 return; 2262 } 2263 2264 // We need to find the end of the region. It may continue in the next 2265 // line. 2266 int end_idx = 0; 2267 lpos_T startpos = { 2268 .lnum = current_lnum, 2269 .col = startcol, 2270 }; 2271 lpos_T endpos = { 0 }; 2272 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos, 2273 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch); 2274 2275 if (endpos.lnum == 0) { 2276 // No end pattern matched. 2277 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE) { 2278 // a "oneline" never continues in the next line 2279 sip->si_ends = true; 2280 sip->si_m_endpos.lnum = current_lnum; 2281 sip->si_m_endpos.col = syn_getcurline_len(); 2282 } else { 2283 // continues in the next line 2284 sip->si_ends = false; 2285 sip->si_m_endpos.lnum = 0; 2286 } 2287 sip->si_h_endpos = sip->si_m_endpos; 2288 } else { 2289 // match within this line 2290 sip->si_m_endpos = endpos; 2291 sip->si_h_endpos = hl_endpos; 2292 sip->si_eoe_pos = end_endpos; 2293 sip->si_ends = true; 2294 sip->si_end_idx = end_idx; 2295 } 2296 } 2297 2298 // Add a new state to the current state stack. 2299 // It is cleared and the index set to "idx". 2300 static void push_current_state(int idx) 2301 { 2302 stateitem_T *p = GA_APPEND_VIA_PTR(stateitem_T, ¤t_state); 2303 CLEAR_POINTER(p); 2304 p->si_idx = idx; 2305 } 2306 2307 // Remove a state from the current_state stack. 2308 static void pop_current_state(void) 2309 { 2310 if (!GA_EMPTY(¤t_state)) { 2311 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch); 2312 current_state.ga_len--; 2313 } 2314 // after the end of a pattern, try matching a keyword or pattern 2315 next_match_idx = -1; 2316 2317 // if first state with "keepend" is popped, reset keepend_level 2318 if (keepend_level >= current_state.ga_len) { 2319 keepend_level = -1; 2320 } 2321 } 2322 2323 /// Find the end of a start/skip/end syntax region after "startpos". 2324 /// Only checks one line. 2325 /// Also handles a match item that continued from a previous line. 2326 /// If not found, the syntax item continues in the next line. m_endpos->lnum 2327 /// will be 0. 2328 /// If found, the end of the region and the end of the highlighting is 2329 /// computed. 2330 /// 2331 /// @param idx index of the pattern 2332 /// @param startpos where to start looking for an END match 2333 /// @param m_endpos return: end of match 2334 /// @param hl_endpos return: end of highlighting 2335 /// @param flagsp return: flags of matching END 2336 /// @param end_endpos return: end of end pattern match 2337 /// @param end_idx return: group ID for end pat. match, or 0 2338 /// @param start_ext submatches from the start pattern 2339 static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_endpos, int *flagsp, 2340 lpos_T *end_endpos, int *end_idx, reg_extmatch_T *start_ext) 2341 { 2342 synpat_T *spp_skip; 2343 int best_idx; 2344 regmmatch_T regmatch; 2345 regmmatch_T best_regmatch; // startpos/endpos of best match 2346 lpos_T pos; 2347 bool had_match = false; 2348 char buf_chartab[32]; // chartab array for syn option iskeyword 2349 2350 // just in case we are invoked for a keyword 2351 if (idx < 0) { 2352 return; 2353 } 2354 2355 // Check for being called with a START pattern. 2356 // Can happen with a match that continues to the next line, because it 2357 // contained a region. 2358 synpat_T *spp = &(SYN_ITEMS(syn_block)[idx]); 2359 if (spp->sp_type != SPTYPE_START) { 2360 *hl_endpos = *startpos; 2361 return; 2362 } 2363 2364 // Find the SKIP or first END pattern after the last START pattern. 2365 while (true) { 2366 spp = &(SYN_ITEMS(syn_block)[idx]); 2367 if (spp->sp_type != SPTYPE_START) { 2368 break; 2369 } 2370 idx++; 2371 } 2372 2373 // Lookup the SKIP pattern (if present) 2374 if (spp->sp_type == SPTYPE_SKIP) { 2375 spp_skip = spp; 2376 idx++; 2377 } else { 2378 spp_skip = NULL; 2379 } 2380 2381 // Setup external matches for syn_regexec(). 2382 unref_extmatch(re_extmatch_in); 2383 re_extmatch_in = ref_extmatch(start_ext); 2384 2385 colnr_T matchcol = startpos->col; // start looking for a match at sstart 2386 int start_idx = idx; // remember the first END pattern. 2387 best_regmatch.startpos[0].col = 0; // avoid compiler warning 2388 2389 // use syntax iskeyword option 2390 save_chartab(buf_chartab); 2391 2392 while (true) { 2393 // Find end pattern that matches first after "matchcol". 2394 best_idx = -1; 2395 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; idx++) { 2396 int lc_col = matchcol; 2397 2398 spp = &(SYN_ITEMS(syn_block)[idx]); 2399 if (spp->sp_type != SPTYPE_END) { // past last END pattern 2400 break; 2401 } 2402 lc_col -= spp->sp_offsets[SPO_LC_OFF]; 2403 if (lc_col < 0) { 2404 lc_col = 0; 2405 } 2406 2407 regmatch.rmm_ic = spp->sp_ic; 2408 regmatch.regprog = spp->sp_prog; 2409 bool r = syn_regexec(®match, startpos->lnum, lc_col, 2410 IF_SYN_TIME(&spp->sp_time)); 2411 spp->sp_prog = regmatch.regprog; 2412 if (r) { 2413 if (best_idx == -1 || regmatch.startpos[0].col 2414 < best_regmatch.startpos[0].col) { 2415 best_idx = idx; 2416 best_regmatch.startpos[0] = regmatch.startpos[0]; 2417 best_regmatch.endpos[0] = regmatch.endpos[0]; 2418 } 2419 } 2420 } 2421 2422 // If all end patterns have been tried, and there is no match, the 2423 // item continues until end-of-line. 2424 if (best_idx == -1) { 2425 break; 2426 } 2427 2428 // If the skip pattern matches before the end pattern, 2429 // continue searching after the skip pattern. 2430 if (spp_skip != NULL) { 2431 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF]; 2432 2433 if (lc_col < 0) { 2434 lc_col = 0; 2435 } 2436 regmatch.rmm_ic = spp_skip->sp_ic; 2437 regmatch.regprog = spp_skip->sp_prog; 2438 int r = syn_regexec(®match, startpos->lnum, lc_col, 2439 IF_SYN_TIME(&spp_skip->sp_time)); 2440 spp_skip->sp_prog = regmatch.regprog; 2441 if (r && regmatch.startpos[0].col <= best_regmatch.startpos[0].col) { 2442 // Add offset to skip pattern match 2443 syn_add_end_off(&pos, ®match, spp_skip, SPO_ME_OFF, 1); 2444 2445 // If the skip pattern goes on to the next line, there is no 2446 // match with an end pattern in this line. 2447 if (pos.lnum > startpos->lnum) { 2448 break; 2449 } 2450 2451 int line_len = ml_get_buf_len(syn_buf, startpos->lnum); 2452 2453 // take care of an empty match or negative offset 2454 if (pos.col <= matchcol) { 2455 matchcol++; 2456 } else if (pos.col <= regmatch.endpos[0].col) { 2457 matchcol = pos.col; 2458 } else { 2459 // Be careful not to jump over the NUL at the end-of-line 2460 for (matchcol = regmatch.endpos[0].col; 2461 matchcol < line_len && matchcol < pos.col; 2462 matchcol++) {} 2463 } 2464 2465 // if the skip pattern includes end-of-line, break here 2466 if (matchcol >= line_len) { 2467 break; 2468 } 2469 2470 continue; // start with first end pattern again 2471 } 2472 } 2473 2474 // Match from start pattern to end pattern. 2475 // Correct for match and highlight offset of end pattern. 2476 spp = &(SYN_ITEMS(syn_block)[best_idx]); 2477 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1); 2478 // can't end before the start 2479 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col) { 2480 m_endpos->col = startpos->col; 2481 } 2482 2483 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1); 2484 // can't end before the start 2485 if (end_endpos->lnum == startpos->lnum 2486 && end_endpos->col < startpos->col) { 2487 end_endpos->col = startpos->col; 2488 } 2489 // can't end after the match 2490 limit_pos(end_endpos, m_endpos); 2491 2492 // If the end group is highlighted differently, adjust the pointers. 2493 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0) { 2494 *end_idx = best_idx; 2495 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT))) { 2496 hl_endpos->lnum = best_regmatch.endpos[0].lnum; 2497 hl_endpos->col = best_regmatch.endpos[0].col; 2498 } else { 2499 hl_endpos->lnum = best_regmatch.startpos[0].lnum; 2500 hl_endpos->col = best_regmatch.startpos[0].col; 2501 } 2502 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF]; 2503 2504 // can't end before the start 2505 if (hl_endpos->lnum == startpos->lnum 2506 && hl_endpos->col < startpos->col) { 2507 hl_endpos->col = startpos->col; 2508 } 2509 limit_pos(hl_endpos, m_endpos); 2510 2511 // now the match ends where the highlighting ends, it is turned 2512 // into the matchgroup for the end 2513 *m_endpos = *hl_endpos; 2514 } else { 2515 *end_idx = 0; 2516 *hl_endpos = *end_endpos; 2517 } 2518 2519 *flagsp = spp->sp_flags; 2520 2521 had_match = true; 2522 break; 2523 } 2524 2525 // no match for an END pattern in this line 2526 if (!had_match) { 2527 m_endpos->lnum = 0; 2528 } 2529 2530 restore_chartab(buf_chartab); 2531 2532 // Remove external matches. 2533 unref_extmatch(re_extmatch_in); 2534 re_extmatch_in = NULL; 2535 } 2536 2537 // Limit "pos" not to be after "limit". 2538 static void limit_pos(lpos_T *pos, lpos_T *limit) 2539 { 2540 if (pos->lnum > limit->lnum) { 2541 *pos = *limit; 2542 } else if (pos->lnum == limit->lnum && pos->col > limit->col) { 2543 pos->col = limit->col; 2544 } 2545 } 2546 2547 // Limit "pos" not to be after "limit", unless pos->lnum is zero. 2548 static void limit_pos_zero(lpos_T *pos, lpos_T *limit) 2549 { 2550 if (pos->lnum == 0) { 2551 *pos = *limit; 2552 } else { 2553 limit_pos(pos, limit); 2554 } 2555 } 2556 2557 /// Add offset to matched text for end of match or highlight. 2558 /// 2559 /// @param result returned position 2560 /// @param regmatch start/end of match 2561 /// @param spp matched pattern 2562 /// @param idx index of offset 2563 /// @param extra extra chars for offset to start 2564 static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, 2565 int extra) 2566 { 2567 int col; 2568 int off; 2569 char *base; 2570 char *p; 2571 2572 if (spp->sp_off_flags & (1 << idx)) { 2573 result->lnum = regmatch->startpos[0].lnum; 2574 col = regmatch->startpos[0].col; 2575 off = spp->sp_offsets[idx] + extra; 2576 } else { 2577 result->lnum = regmatch->endpos[0].lnum; 2578 col = regmatch->endpos[0].col; 2579 off = spp->sp_offsets[idx]; 2580 } 2581 // Don't go past the end of the line. Matters for "rs=e+2" when there 2582 // is a matchgroup. Watch out for match with last NL in the buffer. 2583 if (result->lnum > syn_buf->b_ml.ml_line_count) { 2584 col = 0; 2585 } else if (off != 0) { 2586 base = ml_get_buf(syn_buf, result->lnum); 2587 p = base + col; 2588 if (off > 0) { 2589 while (off-- > 0 && *p != NUL) { 2590 MB_PTR_ADV(p); 2591 } 2592 } else { 2593 while (off++ < 0 && base < p) { 2594 MB_PTR_BACK(base, p); 2595 } 2596 } 2597 col = (int)(p - base); 2598 } 2599 result->col = col; 2600 } 2601 2602 /// Add offset to matched text for start of match or highlight. 2603 /// Avoid resulting column to become negative. 2604 /// 2605 /// @param result returned position 2606 /// @param regmatch start/end of match 2607 /// @param extra extra chars for offset to end 2608 static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, 2609 int extra) 2610 { 2611 int col; 2612 int off; 2613 char *base; 2614 char *p; 2615 2616 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT))) { 2617 result->lnum = regmatch->endpos[0].lnum; 2618 col = regmatch->endpos[0].col; 2619 off = spp->sp_offsets[idx] + extra; 2620 } else { 2621 result->lnum = regmatch->startpos[0].lnum; 2622 col = regmatch->startpos[0].col; 2623 off = spp->sp_offsets[idx]; 2624 } 2625 if (result->lnum > syn_buf->b_ml.ml_line_count) { 2626 // a "\n" at the end of the pattern may take us below the last line 2627 result->lnum = syn_buf->b_ml.ml_line_count; 2628 col = ml_get_buf_len(syn_buf, result->lnum); 2629 } 2630 if (off != 0) { 2631 base = ml_get_buf(syn_buf, result->lnum); 2632 p = base + col; 2633 if (off > 0) { 2634 while (off-- && *p != NUL) { 2635 MB_PTR_ADV(p); 2636 } 2637 } else { 2638 while (off++ && base < p) { 2639 MB_PTR_BACK(base, p); 2640 } 2641 } 2642 col = (int)(p - base); 2643 } 2644 result->col = col; 2645 } 2646 2647 /// Get current line in syntax buffer. 2648 static char *syn_getcurline(void) 2649 { 2650 return ml_get_buf(syn_buf, current_lnum); 2651 } 2652 2653 /// Get length of current line in syntax buffer. 2654 static colnr_T syn_getcurline_len(void) 2655 { 2656 return ml_get_buf_len(syn_buf, current_lnum); 2657 } 2658 2659 // Call vim_regexec() to find a match with "rmp" in "syn_buf". 2660 // Returns true when there is a match. 2661 static bool syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st) 2662 { 2663 int timed_out = 0; 2664 proftime_T pt; 2665 const bool l_syn_time_on = syn_time_on; 2666 2667 if (l_syn_time_on) { 2668 pt = profile_start(); 2669 } 2670 2671 if (rmp->regprog == NULL) { 2672 // This can happen if a previous call to vim_regexec_multi() tried to 2673 // use the NFA engine, which resulted in NFA_TOO_EXPENSIVE, and 2674 // compiling the pattern with the other engine fails. 2675 return false; 2676 } 2677 2678 rmp->rmm_maxcol = (colnr_T)syn_buf->b_p_smc; 2679 int r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, syn_tm, &timed_out); 2680 2681 if (l_syn_time_on) { 2682 pt = profile_end(pt); 2683 st->total = profile_add(st->total, pt); 2684 if (profile_cmp(pt, st->slowest) < 0) { 2685 st->slowest = pt; 2686 } 2687 st->count++; 2688 if (r > 0) { 2689 st->match++; 2690 } 2691 } 2692 if (timed_out && !syn_win->w_s->b_syn_slow) { 2693 syn_win->w_s->b_syn_slow = true; 2694 msg(_("'redrawtime' exceeded, syntax highlighting disabled"), 0); 2695 } 2696 2697 if (r > 0) { 2698 rmp->startpos[0].lnum += lnum; 2699 rmp->endpos[0].lnum += lnum; 2700 return true; 2701 } 2702 return false; 2703 } 2704 2705 /// Check one position in a line for a matching keyword. 2706 /// The caller must check if a keyword can start at startcol. 2707 /// Return its ID if found, 0 otherwise. 2708 /// 2709 /// @param startcol position in line to check for keyword 2710 /// @param endcolp return: character after found keyword 2711 /// @param flagsp return: flags of matching keyword 2712 /// @param next_listp return: next_list of matching keyword 2713 /// @param cur_si item at the top of the stack 2714 /// @param ccharp conceal substitution char 2715 static int check_keyword_id(char *const line, const int startcol, int *const endcolp, 2716 int *const flagsp, int16_t **const next_listp, 2717 stateitem_T *const cur_si, int *const ccharp) 2718 { 2719 // Find first character after the keyword. First character was already 2720 // checked. 2721 char *const kwp = line + startcol; 2722 int kwlen = 0; 2723 do { 2724 kwlen += utfc_ptr2len(kwp + kwlen); 2725 } while (vim_iswordp_buf(kwp + kwlen, syn_buf)); 2726 2727 if (kwlen > MAXKEYWLEN) { 2728 return 0; 2729 } 2730 2731 // Must make a copy of the keyword, so we can add a NUL and make it 2732 // lowercase. 2733 char keyword[MAXKEYWLEN + 1]; // assume max. keyword len is 80 2734 xmemcpyz(keyword, kwp, (size_t)kwlen); 2735 2736 keyentry_T *kp = NULL; 2737 2738 // matching case 2739 if (syn_block->b_keywtab.ht_used != 0) { 2740 kp = match_keyword(keyword, &syn_block->b_keywtab, cur_si); 2741 } 2742 2743 // ignoring case 2744 if (kp == NULL && syn_block->b_keywtab_ic.ht_used != 0) { 2745 str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1); 2746 kp = match_keyword(keyword, &syn_block->b_keywtab_ic, cur_si); 2747 } 2748 2749 if (kp != NULL) { 2750 *endcolp = startcol + kwlen; 2751 *flagsp = kp->flags; 2752 *next_listp = kp->next_list; 2753 *ccharp = kp->k_char; 2754 return kp->k_syn.id; 2755 } 2756 2757 return 0; 2758 } 2759 2760 /// Find keywords that match. There can be several with different 2761 /// attributes. 2762 /// When current_next_list is non-zero accept only that group, otherwise: 2763 /// Accept a not-contained keyword at toplevel. 2764 /// Accept a keyword at other levels only if it is in the contains list. 2765 static keyentry_T *match_keyword(char *keyword, hashtab_T *ht, stateitem_T *cur_si) 2766 { 2767 hashitem_T *hi = hash_find(ht, keyword); 2768 if (!HASHITEM_EMPTY(hi)) { 2769 for (keyentry_T *kp = HI2KE(hi); kp != NULL; kp = kp->ke_next) { 2770 if (current_next_list != 0 2771 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0) 2772 : (cur_si == NULL 2773 ? !(kp->flags & HL_CONTAINED) 2774 : in_id_list(cur_si, cur_si->si_cont_list, 2775 &kp->k_syn, kp->flags))) { 2776 return kp; 2777 } 2778 } 2779 } 2780 return NULL; 2781 } 2782 2783 // Handle ":syntax conceal" command. 2784 static void syn_cmd_conceal(exarg_T *eap, int syncing) 2785 { 2786 char *arg = eap->arg; 2787 char *next; 2788 2789 eap->nextcmd = find_nextcmd(arg); 2790 if (eap->skip) { 2791 return; 2792 } 2793 2794 next = skiptowhite(arg); 2795 if (*arg == NUL) { 2796 if (curwin->w_s->b_syn_conceal) { 2797 msg("syntax conceal on", 0); 2798 } else { 2799 msg("syntax conceal off", 0); 2800 } 2801 } else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2) { 2802 curwin->w_s->b_syn_conceal = true; 2803 } else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3) { 2804 curwin->w_s->b_syn_conceal = false; 2805 } else { 2806 semsg(_(e_illegal_arg), arg); 2807 } 2808 } 2809 2810 /// Handle ":syntax case" command. 2811 static void syn_cmd_case(exarg_T *eap, int syncing) 2812 { 2813 char *arg = eap->arg; 2814 char *next; 2815 2816 eap->nextcmd = find_nextcmd(arg); 2817 if (eap->skip) { 2818 return; 2819 } 2820 2821 next = skiptowhite(arg); 2822 if (*arg == NUL) { 2823 if (curwin->w_s->b_syn_ic) { 2824 msg("syntax case ignore", 0); 2825 } else { 2826 msg("syntax case match", 0); 2827 } 2828 } else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5) { 2829 curwin->w_s->b_syn_ic = false; 2830 } else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6) { 2831 curwin->w_s->b_syn_ic = true; 2832 } else { 2833 semsg(_(e_illegal_arg), arg); 2834 } 2835 } 2836 2837 /// Handle ":syntax foldlevel" command. 2838 static void syn_cmd_foldlevel(exarg_T *eap, int syncing) 2839 { 2840 char *arg = eap->arg; 2841 char *arg_end; 2842 2843 eap->nextcmd = find_nextcmd(arg); 2844 if (eap->skip) { 2845 return; 2846 } 2847 2848 if (*arg == NUL) { 2849 switch (curwin->w_s->b_syn_foldlevel) { 2850 case SYNFLD_START: 2851 msg("syntax foldlevel start", 0); break; 2852 case SYNFLD_MINIMUM: 2853 msg("syntax foldlevel minimum", 0); break; 2854 default: 2855 break; 2856 } 2857 return; 2858 } 2859 2860 arg_end = skiptowhite(arg); 2861 if (STRNICMP(arg, "start", 5) == 0 && arg_end - arg == 5) { 2862 curwin->w_s->b_syn_foldlevel = SYNFLD_START; 2863 } else if (STRNICMP(arg, "minimum", 7) == 0 && arg_end - arg == 7) { 2864 curwin->w_s->b_syn_foldlevel = SYNFLD_MINIMUM; 2865 } else { 2866 semsg(_(e_illegal_arg), arg); 2867 return; 2868 } 2869 2870 arg = skipwhite(arg_end); 2871 if (*arg != NUL) { 2872 semsg(_(e_illegal_arg), arg); 2873 } 2874 } 2875 2876 /// Handle ":syntax spell" command. 2877 static void syn_cmd_spell(exarg_T *eap, int syncing) 2878 { 2879 char *arg = eap->arg; 2880 char *next; 2881 2882 eap->nextcmd = find_nextcmd(arg); 2883 if (eap->skip) { 2884 return; 2885 } 2886 2887 next = skiptowhite(arg); 2888 if (*arg == NUL) { 2889 if (curwin->w_s->b_syn_spell == SYNSPL_TOP) { 2890 msg("syntax spell toplevel", 0); 2891 } else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP) { 2892 msg("syntax spell notoplevel", 0); 2893 } else { 2894 msg("syntax spell default", 0); 2895 } 2896 } else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8) { 2897 curwin->w_s->b_syn_spell = SYNSPL_TOP; 2898 } else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10) { 2899 curwin->w_s->b_syn_spell = SYNSPL_NOTOP; 2900 } else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7) { 2901 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT; 2902 } else { 2903 semsg(_(e_illegal_arg), arg); 2904 return; 2905 } 2906 2907 // assume spell checking changed, force a redraw 2908 redraw_later(curwin, UPD_NOT_VALID); 2909 } 2910 2911 /// Handle ":syntax iskeyword" command. 2912 static void syn_cmd_iskeyword(exarg_T *eap, int syncing) 2913 { 2914 char *arg = eap->arg; 2915 char save_chartab[32]; 2916 char *save_isk; 2917 2918 if (eap->skip) { 2919 return; 2920 } 2921 2922 arg = skipwhite(arg); 2923 if (*arg == NUL) { 2924 msg_puts("\n"); 2925 if (curwin->w_s->b_syn_isk != empty_string_option) { 2926 msg_puts("syntax iskeyword "); 2927 msg_outtrans(curwin->w_s->b_syn_isk, 0, false); 2928 } else { 2929 msg_outtrans(_("syntax iskeyword not set"), 0, false); 2930 } 2931 } else { 2932 if (STRNICMP(arg, "clear", 5) == 0) { 2933 memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab, (size_t)32); 2934 clear_string_option(&curwin->w_s->b_syn_isk); 2935 } else { 2936 memmove(save_chartab, curbuf->b_chartab, (size_t)32); 2937 save_isk = curbuf->b_p_isk; 2938 curbuf->b_p_isk = xstrdup(arg); 2939 2940 buf_init_chartab(curbuf, false); 2941 memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab, (size_t)32); 2942 memmove(curbuf->b_chartab, save_chartab, (size_t)32); 2943 clear_string_option(&curwin->w_s->b_syn_isk); 2944 curwin->w_s->b_syn_isk = curbuf->b_p_isk; 2945 curbuf->b_p_isk = save_isk; 2946 } 2947 } 2948 redraw_later(curwin, UPD_NOT_VALID); 2949 } 2950 2951 // Clear all syntax info for one buffer. 2952 void syntax_clear(synblock_T *block) 2953 { 2954 block->b_syn_error = false; // clear previous error 2955 block->b_syn_slow = false; // clear previous timeout 2956 block->b_syn_ic = false; // Use case, by default 2957 block->b_syn_foldlevel = SYNFLD_START; 2958 block->b_syn_spell = SYNSPL_DEFAULT; // default spell checking 2959 block->b_syn_containedin = false; 2960 block->b_syn_conceal = false; 2961 2962 // free the keywords 2963 clear_keywtab(&block->b_keywtab); 2964 clear_keywtab(&block->b_keywtab_ic); 2965 2966 // free the syntax patterns 2967 for (int i = block->b_syn_patterns.ga_len; --i >= 0;) { 2968 syn_clear_pattern(block, i); 2969 } 2970 ga_clear(&block->b_syn_patterns); 2971 2972 // free the syntax clusters 2973 for (int i = block->b_syn_clusters.ga_len; --i >= 0;) { 2974 syn_clear_cluster(block, i); 2975 } 2976 ga_clear(&block->b_syn_clusters); 2977 block->b_spell_cluster_id = 0; 2978 block->b_nospell_cluster_id = 0; 2979 2980 block->b_syn_sync_flags = 0; 2981 block->b_syn_sync_minlines = 0; 2982 block->b_syn_sync_maxlines = 0; 2983 block->b_syn_sync_linebreaks = 0; 2984 2985 vim_regfree(block->b_syn_linecont_prog); 2986 block->b_syn_linecont_prog = NULL; 2987 XFREE_CLEAR(block->b_syn_linecont_pat); 2988 block->b_syn_folditems = 0; 2989 clear_string_option(&block->b_syn_isk); 2990 2991 // free the stored states 2992 syn_stack_free_all(block); 2993 invalidate_current_state(); 2994 2995 // Reset the counter for ":syn include" 2996 running_syn_inc_tag = 0; 2997 } 2998 2999 // Get rid of ownsyntax for window "wp". 3000 void reset_synblock(win_T *wp) 3001 { 3002 if (wp->w_s != &wp->w_buffer->b_s) { 3003 syntax_clear(wp->w_s); 3004 xfree(wp->w_s); 3005 wp->w_s = &wp->w_buffer->b_s; 3006 } 3007 } 3008 3009 // Clear syncing info for one buffer. 3010 static void syntax_sync_clear(void) 3011 { 3012 // free the syntax patterns 3013 for (int i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0;) { 3014 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing) { 3015 syn_remove_pattern(curwin->w_s, i); 3016 } 3017 } 3018 3019 curwin->w_s->b_syn_sync_flags = 0; 3020 curwin->w_s->b_syn_sync_minlines = 0; 3021 curwin->w_s->b_syn_sync_maxlines = 0; 3022 curwin->w_s->b_syn_sync_linebreaks = 0; 3023 3024 vim_regfree(curwin->w_s->b_syn_linecont_prog); 3025 curwin->w_s->b_syn_linecont_prog = NULL; 3026 XFREE_CLEAR(curwin->w_s->b_syn_linecont_pat); 3027 clear_string_option(&curwin->w_s->b_syn_isk); 3028 3029 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax. 3030 } 3031 3032 // Remove one pattern from the buffer's pattern list. 3033 static void syn_remove_pattern(synblock_T *block, int idx) 3034 { 3035 synpat_T *spp; 3036 3037 spp = &(SYN_ITEMS(block)[idx]); 3038 if (spp->sp_flags & HL_FOLD) { 3039 block->b_syn_folditems--; 3040 } 3041 syn_clear_pattern(block, idx); 3042 memmove(spp, spp + 1, sizeof(synpat_T) * (size_t)(block->b_syn_patterns.ga_len - idx - 1)); 3043 block->b_syn_patterns.ga_len--; 3044 } 3045 3046 // Clear and free one syntax pattern. When clearing all, must be called from 3047 // last to first! 3048 static void syn_clear_pattern(synblock_T *block, int i) 3049 { 3050 xfree(SYN_ITEMS(block)[i].sp_pattern); 3051 vim_regfree(SYN_ITEMS(block)[i].sp_prog); 3052 // Only free sp_cont_list and sp_next_list of first start pattern 3053 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START) { 3054 xfree(SYN_ITEMS(block)[i].sp_cont_list); 3055 xfree(SYN_ITEMS(block)[i].sp_next_list); 3056 xfree(SYN_ITEMS(block)[i].sp_syn.cont_in_list); 3057 } 3058 } 3059 3060 // Clear and free one syntax cluster. 3061 static void syn_clear_cluster(synblock_T *block, int i) 3062 { 3063 xfree(SYN_CLSTR(block)[i].scl_name); 3064 xfree(SYN_CLSTR(block)[i].scl_name_u); 3065 xfree(SYN_CLSTR(block)[i].scl_list); 3066 } 3067 3068 /// Handle ":syntax clear" command. 3069 static void syn_cmd_clear(exarg_T *eap, int syncing) 3070 { 3071 char *arg = eap->arg; 3072 char *arg_end; 3073 int id; 3074 3075 eap->nextcmd = find_nextcmd(arg); 3076 if (eap->skip) { 3077 return; 3078 } 3079 3080 // We have to disable this within ":syn include @group filename", 3081 // because otherwise @group would get deleted. 3082 // Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn 3083 // clear". 3084 if (curwin->w_s->b_syn_topgrp != 0) { 3085 return; 3086 } 3087 3088 if (ends_excmd(*arg)) { 3089 // No argument: Clear all syntax items. 3090 if (syncing) { 3091 syntax_sync_clear(); 3092 } else { 3093 syntax_clear(curwin->w_s); 3094 if (curwin->w_s == &curwin->w_buffer->b_s) { 3095 do_unlet(S_LEN("b:current_syntax"), true); 3096 } 3097 do_unlet(S_LEN("w:current_syntax"), true); 3098 } 3099 } else { 3100 // Clear the group IDs that are in the argument. 3101 while (!ends_excmd(*arg)) { 3102 arg_end = skiptowhite(arg); 3103 if (*arg == '@') { 3104 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1)); 3105 if (id == 0) { 3106 semsg(_("E391: No such syntax cluster: %s"), arg); 3107 break; 3108 } 3109 // We can't physically delete a cluster without changing 3110 // the IDs of other clusters, so we do the next best thing 3111 // and make it empty. 3112 int scl_id = id - SYNID_CLUSTER; 3113 3114 XFREE_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list); 3115 } else { 3116 id = syn_name2id_len(arg, (size_t)(arg_end - arg)); 3117 if (id == 0) { 3118 semsg(_(e_nogroup), arg); 3119 break; 3120 } 3121 syn_clear_one(id, syncing); 3122 } 3123 arg = skipwhite(arg_end); 3124 } 3125 } 3126 redraw_curbuf_later(UPD_SOME_VALID); 3127 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax. 3128 } 3129 3130 // Clear one syntax group for the current buffer. 3131 static void syn_clear_one(const int id, const bool syncing) 3132 { 3133 synpat_T *spp; 3134 3135 // Clear keywords only when not ":syn sync clear group-name" 3136 if (!syncing) { 3137 syn_clear_keyword(id, &curwin->w_s->b_keywtab); 3138 syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic); 3139 } 3140 3141 // clear the patterns for "id" 3142 for (int idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0;) { 3143 spp = &(SYN_ITEMS(curwin->w_s)[idx]); 3144 if (spp->sp_syn.id != id || spp->sp_syncing != syncing) { 3145 continue; 3146 } 3147 syn_remove_pattern(curwin->w_s, idx); 3148 } 3149 } 3150 3151 // Handle ":syntax on" command. 3152 static void syn_cmd_on(exarg_T *eap, int syncing) 3153 { 3154 syn_cmd_onoff(eap, "syntax"); 3155 } 3156 3157 // Handle ":syntax reset" command. 3158 // It actually resets highlighting, not syntax. 3159 static void syn_cmd_reset(exarg_T *eap, int syncing) 3160 { 3161 eap->nextcmd = check_nextcmd(eap->arg); 3162 if (!eap->skip) { 3163 init_highlight(true, true); 3164 } 3165 } 3166 3167 // Handle ":syntax manual" command. 3168 static void syn_cmd_manual(exarg_T *eap, int syncing) 3169 { 3170 syn_cmd_onoff(eap, "manual"); 3171 } 3172 3173 // Handle ":syntax off" command. 3174 static void syn_cmd_off(exarg_T *eap, int syncing) 3175 { 3176 syn_cmd_onoff(eap, "nosyntax"); 3177 } 3178 3179 static void syn_cmd_onoff(exarg_T *eap, char *name) 3180 FUNC_ATTR_NONNULL_ALL 3181 { 3182 eap->nextcmd = check_nextcmd(eap->arg); 3183 if (!eap->skip) { 3184 did_syntax_onoff = true; 3185 char buf[100]; 3186 memcpy(buf, "so ", 4); 3187 vim_snprintf(buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name); 3188 do_cmdline_cmd(buf); 3189 } 3190 } 3191 3192 void syn_maybe_enable(void) 3193 { 3194 if (!did_syntax_onoff) { 3195 exarg_T ea; 3196 ea.arg = ""; 3197 ea.skip = false; 3198 syn_cmd_on(&ea, false); 3199 } 3200 } 3201 3202 /// Handle ":syntax [list]" command: list current syntax words. 3203 /// 3204 /// @param syncing when true: list syncing items 3205 static void syn_cmd_list(exarg_T *eap, int syncing) 3206 { 3207 char *arg = eap->arg; 3208 char *arg_end; 3209 3210 eap->nextcmd = find_nextcmd(arg); 3211 if (eap->skip) { 3212 return; 3213 } 3214 3215 msg_ext_set_kind("list_cmd"); 3216 if (!syntax_present(curwin)) { 3217 msg(_(msg_no_items), 0); 3218 return; 3219 } 3220 3221 if (syncing) { 3222 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT) { 3223 msg_puts(_("syncing on C-style comments")); 3224 syn_lines_msg(); 3225 syn_match_msg(); 3226 return; 3227 } else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH)) { 3228 if (curwin->w_s->b_syn_sync_minlines == 0) { 3229 msg_puts(_("no syncing")); 3230 } else { 3231 if (curwin->w_s->b_syn_sync_minlines == MAXLNUM) { 3232 msg_puts(_("syncing starts at the first line")); 3233 } else { 3234 msg_puts(_("syncing starts ")); 3235 msg_outnum(curwin->w_s->b_syn_sync_minlines); 3236 msg_puts(_(" lines before top line")); 3237 } 3238 syn_match_msg(); 3239 } 3240 return; 3241 } 3242 msg_puts_title(_("\n--- Syntax sync items ---")); 3243 if (curwin->w_s->b_syn_sync_minlines > 0 3244 || curwin->w_s->b_syn_sync_maxlines > 0 3245 || curwin->w_s->b_syn_sync_linebreaks > 0) { 3246 msg_puts(_("\nsyncing on items")); 3247 syn_lines_msg(); 3248 syn_match_msg(); 3249 } 3250 } else { 3251 msg_puts_title(_("\n--- Syntax items ---")); 3252 } 3253 if (ends_excmd(*arg)) { 3254 // No argument: List all group IDs and all syntax clusters. 3255 for (int id = 1; id <= highlight_num_groups() && !got_int; id++) { 3256 syn_list_one(id, syncing, false); 3257 } 3258 for (int id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; id++) { 3259 syn_list_cluster(id); 3260 } 3261 } else { 3262 // List the group IDs and syntax clusters that are in the argument. 3263 while (!ends_excmd(*arg) && !got_int) { 3264 arg_end = skiptowhite(arg); 3265 if (*arg == '@') { 3266 int id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1)); 3267 if (id == 0) { 3268 semsg(_("E392: No such syntax cluster: %s"), arg); 3269 } else { 3270 syn_list_cluster(id - SYNID_CLUSTER); 3271 } 3272 } else { 3273 int id = syn_name2id_len(arg, (size_t)(arg_end - arg)); 3274 if (id == 0) { 3275 semsg(_(e_nogroup), arg); 3276 } else { 3277 syn_list_one(id, syncing, true); 3278 } 3279 } 3280 arg = skipwhite(arg_end); 3281 } 3282 } 3283 eap->nextcmd = check_nextcmd(arg); 3284 } 3285 3286 static void syn_lines_msg(void) 3287 { 3288 if (curwin->w_s->b_syn_sync_maxlines > 0 3289 || curwin->w_s->b_syn_sync_minlines > 0) { 3290 msg_puts("; "); 3291 if (curwin->w_s->b_syn_sync_minlines == MAXLNUM) { 3292 msg_puts(_("from the first line")); 3293 } else { 3294 if (curwin->w_s->b_syn_sync_minlines > 0) { 3295 msg_puts(_("minimal ")); 3296 msg_outnum(curwin->w_s->b_syn_sync_minlines); 3297 if (curwin->w_s->b_syn_sync_maxlines) { 3298 msg_puts(", "); 3299 } 3300 } 3301 if (curwin->w_s->b_syn_sync_maxlines > 0) { 3302 msg_puts(_("maximal ")); 3303 msg_outnum(curwin->w_s->b_syn_sync_maxlines); 3304 } 3305 msg_puts(_(" lines before top line")); 3306 } 3307 } 3308 } 3309 3310 static void syn_match_msg(void) 3311 { 3312 if (curwin->w_s->b_syn_sync_linebreaks > 0) { 3313 msg_puts(_("; match ")); 3314 msg_outnum(curwin->w_s->b_syn_sync_linebreaks); 3315 msg_puts(_(" line breaks")); 3316 } 3317 } 3318 3319 static int last_matchgroup; 3320 3321 /// List one syntax item, for ":syntax" or "syntax list syntax_name". 3322 /// 3323 /// @param syncing when true: list syncing items 3324 /// @param link_only when true; list link-only too 3325 static void syn_list_one(const int id, const bool syncing, const bool link_only) 3326 { 3327 bool did_header = false; 3328 static keyvalue_T namelist1[] = { 3329 KEYVALUE_ENTRY(HL_DISPLAY, "display"), 3330 KEYVALUE_ENTRY(HL_CONTAINED, "contained"), 3331 KEYVALUE_ENTRY(HL_ONELINE, "oneline"), 3332 KEYVALUE_ENTRY(HL_KEEPEND, "keepend"), 3333 KEYVALUE_ENTRY(HL_EXTEND, "extend"), 3334 KEYVALUE_ENTRY(HL_EXCLUDENL, "excludenl"), 3335 KEYVALUE_ENTRY(HL_TRANSP, "transparent"), 3336 KEYVALUE_ENTRY(HL_FOLD, "fold"), 3337 KEYVALUE_ENTRY(HL_CONCEAL, "conceal"), 3338 KEYVALUE_ENTRY(HL_CONCEALENDS, "concealends"), 3339 }; 3340 static keyvalue_T namelist2[] = { 3341 KEYVALUE_ENTRY(HL_SKIPWHITE, "skipwhite"), 3342 KEYVALUE_ENTRY(HL_SKIPNL, "skipnl"), 3343 KEYVALUE_ENTRY(HL_SKIPEMPTY, "skipempty"), 3344 }; 3345 3346 const int hl_id = HLF_D; // highlight like directories 3347 3348 // list the keywords for "id" 3349 if (!syncing) { 3350 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, false, hl_id); 3351 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic, did_header, hl_id); 3352 } 3353 3354 // list the patterns for "id" 3355 for (int idx = 0; 3356 idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; 3357 idx++) { 3358 const synpat_T *const spp = &(SYN_ITEMS(curwin->w_s)[idx]); 3359 if (spp->sp_syn.id != id || spp->sp_syncing != syncing) { 3360 continue; 3361 } 3362 3363 syn_list_header(did_header, 0, id, true); 3364 did_header = true; 3365 last_matchgroup = 0; 3366 if (spp->sp_type == SPTYPE_MATCH) { 3367 put_pattern("match", ' ', spp, hl_id); 3368 msg_putchar(' '); 3369 } else if (spp->sp_type == SPTYPE_START) { 3370 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START) { 3371 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], hl_id); 3372 } 3373 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP) { 3374 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], hl_id); 3375 } 3376 while (idx < curwin->w_s->b_syn_patterns.ga_len 3377 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END) { 3378 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], hl_id); 3379 } 3380 idx--; 3381 msg_putchar(' '); 3382 } 3383 syn_list_flags(namelist1, ARRAY_SIZE(namelist1), spp->sp_flags, hl_id); 3384 3385 if (spp->sp_cont_list != NULL) { 3386 put_id_list("contains", spp->sp_cont_list, hl_id); 3387 } 3388 3389 if (spp->sp_syn.cont_in_list != NULL) { 3390 put_id_list("containedin", spp->sp_syn.cont_in_list, hl_id); 3391 } 3392 3393 if (spp->sp_next_list != NULL) { 3394 put_id_list("nextgroup", spp->sp_next_list, hl_id); 3395 syn_list_flags(namelist2, ARRAY_SIZE(namelist2), spp->sp_flags, hl_id); 3396 } 3397 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE)) { 3398 if (spp->sp_flags & HL_SYNC_HERE) { 3399 msg_puts_hl("grouphere", hl_id, false); 3400 } else { 3401 msg_puts_hl("groupthere", hl_id, false); 3402 } 3403 msg_putchar(' '); 3404 if (spp->sp_sync_idx >= 0) { 3405 msg_outtrans(highlight_group_name(SYN_ITEMS(curwin->w_s) 3406 [spp->sp_sync_idx].sp_syn.id - 1), 0, false); 3407 } else { 3408 msg_puts("NONE"); 3409 } 3410 msg_putchar(' '); 3411 } 3412 } 3413 3414 // list the link, if there is one 3415 if (highlight_link_id(id - 1) && (did_header || link_only) && !got_int) { 3416 syn_list_header(did_header, 0, id, true); 3417 msg_puts_hl("links to", hl_id, false); 3418 msg_putchar(' '); 3419 msg_outtrans(highlight_group_name(highlight_link_id(id - 1) - 1), 0, false); 3420 } 3421 } 3422 3423 static void syn_list_flags(keyvalue_T *nlist, size_t nr_entries, int flags, int hl_id) 3424 { 3425 for (size_t i = 0; i < nr_entries; i++) { 3426 if (flags & nlist[i].key) { 3427 msg_puts_hl(nlist[i].value, hl_id, false); 3428 msg_putchar(' '); 3429 } 3430 } 3431 } 3432 3433 // List one syntax cluster, for ":syntax" or "syntax list syntax_name". 3434 static void syn_list_cluster(int id) 3435 { 3436 int endcol = 15; 3437 3438 // slight hack: roughly duplicate the guts of syn_list_header() 3439 msg_putchar('\n'); 3440 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name, 0, false); 3441 3442 if (msg_col >= endcol) { // output at least one space 3443 endcol = msg_col + 1; 3444 } 3445 if (Columns <= endcol) { // avoid hang for tiny window 3446 endcol = Columns - 1; 3447 } 3448 3449 msg_advance(endcol); 3450 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL) { 3451 put_id_list("cluster", SYN_CLSTR(curwin->w_s)[id].scl_list, HLF_D); 3452 } else { 3453 msg_puts_hl("cluster", HLF_D, false); 3454 msg_puts("=NONE"); 3455 } 3456 } 3457 3458 static void put_id_list(const char *const name, const int16_t *const list, const int hl_id) 3459 { 3460 msg_puts_hl(name, hl_id, false); 3461 msg_putchar('='); 3462 for (const int16_t *p = list; *p; p++) { 3463 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP) { 3464 if (p[1]) { 3465 msg_puts("ALLBUT"); 3466 } else { 3467 msg_puts("ALL"); 3468 } 3469 } else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED) { 3470 msg_puts("TOP"); 3471 } else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER) { 3472 msg_puts("CONTAINED"); 3473 } else if (*p >= SYNID_CLUSTER) { 3474 int scl_id = *p - SYNID_CLUSTER; 3475 3476 msg_putchar('@'); 3477 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name, 0, false); 3478 } else { 3479 msg_outtrans(highlight_group_name(*p - 1), 0, false); 3480 } 3481 if (p[1]) { 3482 msg_putchar(','); 3483 } 3484 } 3485 msg_putchar(' '); 3486 } 3487 3488 static void put_pattern(const char *const s, const int c, const synpat_T *const spp, 3489 const int hl_id) 3490 { 3491 static const char *const sepchars = "/+=-#@\"|'^&"; 3492 int i; 3493 3494 // May have to write "matchgroup=group" 3495 if (last_matchgroup != spp->sp_syn_match_id) { 3496 last_matchgroup = spp->sp_syn_match_id; 3497 msg_puts_hl("matchgroup", hl_id, false); 3498 msg_putchar('='); 3499 if (last_matchgroup == 0) { 3500 msg_outtrans("NONE", 0, false); 3501 } else { 3502 msg_outtrans(highlight_group_name(last_matchgroup - 1), 0, false); 3503 } 3504 msg_putchar(' '); 3505 } 3506 3507 // Output the name of the pattern and an '=' or ' '. 3508 msg_puts_hl(s, hl_id, false); 3509 msg_putchar(c); 3510 3511 // output the pattern, in between a char that is not in the pattern 3512 for (i = 0; vim_strchr(spp->sp_pattern, (uint8_t)sepchars[i]) != NULL;) { 3513 if (sepchars[++i] == NUL) { 3514 i = 0; // no good char found, just use the first one 3515 break; 3516 } 3517 } 3518 msg_putchar(sepchars[i]); 3519 msg_outtrans(spp->sp_pattern, 0, false); 3520 msg_putchar(sepchars[i]); 3521 3522 // output any pattern options 3523 bool first = true; 3524 for (i = 0; i < SPO_COUNT; i++) { 3525 const int mask = (1 << i); 3526 if (!(spp->sp_off_flags & (mask + (mask << SPO_COUNT)))) { 3527 continue; 3528 } 3529 if (!first) { 3530 msg_putchar(','); // Separate with commas. 3531 } 3532 msg_puts(spo_name_tab[i]); 3533 const int n = spp->sp_offsets[i]; 3534 if (i != SPO_LC_OFF) { 3535 if (spp->sp_off_flags & mask) { 3536 msg_putchar('s'); 3537 } else { 3538 msg_putchar('e'); 3539 } 3540 if (n > 0) { 3541 msg_putchar('+'); 3542 } 3543 } 3544 if (n || i == SPO_LC_OFF) { 3545 msg_outnum(n); 3546 } 3547 first = false; 3548 } 3549 msg_putchar(' '); 3550 } 3551 3552 /// List or clear the keywords for one syntax group. 3553 /// 3554 /// @param did_header header has already been printed 3555 /// 3556 /// @return true if the header has been printed. 3557 static bool syn_list_keywords(const int id, const hashtab_T *const ht, bool did_header, 3558 const int hl_id) 3559 { 3560 int prev_contained = 0; 3561 const int16_t *prev_next_list = NULL; 3562 const int16_t *prev_cont_in_list = NULL; 3563 int prev_skipnl = 0; 3564 int prev_skipwhite = 0; 3565 int prev_skipempty = 0; 3566 3567 // Unfortunately, this list of keywords is not sorted on alphabet but on 3568 // hash value... 3569 size_t todo = ht->ht_used; 3570 for (const hashitem_T *hi = ht->ht_array; todo > 0 && !got_int; hi++) { 3571 if (HASHITEM_EMPTY(hi)) { 3572 continue; 3573 } 3574 todo--; 3575 for (keyentry_T *kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next) { 3576 if (kp->k_syn.id == id) { 3577 int outlen = 0; 3578 bool force_newline = false; 3579 if (prev_contained != (kp->flags & HL_CONTAINED) 3580 || prev_skipnl != (kp->flags & HL_SKIPNL) 3581 || prev_skipwhite != (kp->flags & HL_SKIPWHITE) 3582 || prev_skipempty != (kp->flags & HL_SKIPEMPTY) 3583 || prev_cont_in_list != kp->k_syn.cont_in_list 3584 || prev_next_list != kp->next_list) { 3585 force_newline = true; 3586 } else { 3587 outlen = (int)strlen(kp->keyword); 3588 } 3589 // output "contained" and "nextgroup" on each line 3590 if (syn_list_header(did_header, outlen, id, force_newline)) { 3591 prev_contained = 0; 3592 prev_next_list = NULL; 3593 prev_cont_in_list = NULL; 3594 prev_skipnl = 0; 3595 prev_skipwhite = 0; 3596 prev_skipempty = 0; 3597 } 3598 did_header = true; 3599 if (prev_contained != (kp->flags & HL_CONTAINED)) { 3600 msg_puts_hl("contained", hl_id, false); 3601 msg_putchar(' '); 3602 prev_contained = (kp->flags & HL_CONTAINED); 3603 } 3604 if (kp->k_syn.cont_in_list != prev_cont_in_list) { 3605 put_id_list("containedin", kp->k_syn.cont_in_list, hl_id); 3606 msg_putchar(' '); 3607 prev_cont_in_list = kp->k_syn.cont_in_list; 3608 } 3609 if (kp->next_list != prev_next_list) { 3610 put_id_list("nextgroup", kp->next_list, hl_id); 3611 msg_putchar(' '); 3612 prev_next_list = kp->next_list; 3613 if (kp->flags & HL_SKIPNL) { 3614 msg_puts_hl("skipnl", hl_id, false); 3615 msg_putchar(' '); 3616 prev_skipnl = (kp->flags & HL_SKIPNL); 3617 } 3618 if (kp->flags & HL_SKIPWHITE) { 3619 msg_puts_hl("skipwhite", hl_id, false); 3620 msg_putchar(' '); 3621 prev_skipwhite = (kp->flags & HL_SKIPWHITE); 3622 } 3623 if (kp->flags & HL_SKIPEMPTY) { 3624 msg_puts_hl("skipempty", hl_id, false); 3625 msg_putchar(' '); 3626 prev_skipempty = (kp->flags & HL_SKIPEMPTY); 3627 } 3628 } 3629 msg_outtrans(kp->keyword, 0, false); 3630 } 3631 } 3632 } 3633 3634 return did_header; 3635 } 3636 3637 static void syn_clear_keyword(int id, hashtab_T *ht) 3638 { 3639 hash_lock(ht); 3640 int todo = (int)ht->ht_used; 3641 for (hashitem_T *hi = ht->ht_array; todo > 0; hi++) { 3642 if (HASHITEM_EMPTY(hi)) { 3643 continue; 3644 } 3645 todo--; 3646 keyentry_T *kp_prev = NULL; 3647 for (keyentry_T *kp = HI2KE(hi); kp != NULL;) { 3648 if (kp->k_syn.id == id) { 3649 keyentry_T *kp_next = kp->ke_next; 3650 if (kp_prev == NULL) { 3651 if (kp_next == NULL) { 3652 hash_remove(ht, hi); 3653 } else { 3654 hi->hi_key = KE2HIKEY(kp_next); 3655 } 3656 } else { 3657 kp_prev->ke_next = kp_next; 3658 } 3659 xfree(kp->next_list); 3660 xfree(kp->k_syn.cont_in_list); 3661 xfree(kp); 3662 kp = kp_next; 3663 } else { 3664 kp_prev = kp; 3665 kp = kp->ke_next; 3666 } 3667 } 3668 } 3669 hash_unlock(ht); 3670 } 3671 3672 // Clear a whole keyword table. 3673 static void clear_keywtab(hashtab_T *ht) 3674 { 3675 keyentry_T *kp_next; 3676 3677 int todo = (int)ht->ht_used; 3678 for (hashitem_T *hi = ht->ht_array; todo > 0; hi++) { 3679 if (!HASHITEM_EMPTY(hi)) { 3680 todo--; 3681 for (keyentry_T *kp = HI2KE(hi); kp != NULL; kp = kp_next) { 3682 kp_next = kp->ke_next; 3683 xfree(kp->next_list); 3684 xfree(kp->k_syn.cont_in_list); 3685 xfree(kp); 3686 } 3687 } 3688 } 3689 hash_clear(ht); 3690 hash_init(ht); 3691 } 3692 3693 /// Add a keyword to the list of keywords. 3694 /// 3695 /// @param name name of keyword 3696 /// @param id group ID for this keyword 3697 /// @param flags flags for this keyword 3698 /// @param cont_in_list containedin for this keyword 3699 /// @param next_list nextgroup for this keyword 3700 static void add_keyword(char *const name, size_t namelen, const int id, const int flags, 3701 int16_t *const cont_in_list, int16_t *const next_list, 3702 const int conceal_char) 3703 { 3704 char name_folded[MAXKEYWLEN + 1]; 3705 const char *name_ic; 3706 size_t name_iclen; 3707 if (curwin->w_s->b_syn_ic) { 3708 name_ic = str_foldcase(name, (int)namelen, name_folded, MAXKEYWLEN + 1); 3709 name_iclen = strlen(name_ic); 3710 } else { 3711 name_ic = name; 3712 name_iclen = namelen; 3713 } 3714 3715 keyentry_T *const kp = xmalloc(offsetof(keyentry_T, keyword) + name_iclen + 1); 3716 STRCPY(kp->keyword, name_ic); 3717 kp->k_syn.id = (int16_t)id; 3718 kp->k_syn.inc_tag = current_syn_inc_tag; 3719 kp->flags = flags; 3720 kp->k_char = conceal_char; 3721 kp->k_syn.cont_in_list = copy_id_list(cont_in_list); 3722 if (cont_in_list != NULL) { 3723 curwin->w_s->b_syn_containedin = true; 3724 } 3725 kp->next_list = copy_id_list(next_list); 3726 3727 const hash_T hash = hash_hash(kp->keyword); 3728 hashtab_T *const ht = (curwin->w_s->b_syn_ic) 3729 ? &curwin->w_s->b_keywtab_ic 3730 : &curwin->w_s->b_keywtab; 3731 hashitem_T *const hi = hash_lookup(ht, kp->keyword, 3732 strlen(kp->keyword), hash); 3733 3734 // even though it looks like only the kp->keyword member is 3735 // being used here, vim uses some pointer trickery to get the original 3736 // struct again later by using knowledge of the offset of the keyword 3737 // field in the struct. See the definition of the HI2KE macro. 3738 if (HASHITEM_EMPTY(hi)) { 3739 // new keyword, add to hashtable 3740 kp->ke_next = NULL; 3741 hash_add_item(ht, hi, kp->keyword, hash); 3742 } else { 3743 // keyword already exists, prepend to list 3744 kp->ke_next = HI2KE(hi); 3745 hi->hi_key = KE2HIKEY(kp); 3746 } 3747 } 3748 3749 /// Get the start and end of the group name argument. 3750 /// 3751 /// @param arg start of the argument 3752 /// @param name_end pointer to end of the name 3753 /// 3754 /// @return a pointer to the first argument. 3755 /// Return NULL if the end of the command was found instead of further args. 3756 static char *get_group_name(char *arg, char **name_end) 3757 { 3758 *name_end = skiptowhite(arg); 3759 char *rest = skipwhite(*name_end); 3760 3761 // Check if there are enough arguments. The first argument may be a 3762 // pattern, where '|' is allowed, so only check for NUL. 3763 if (ends_excmd(*arg) || *rest == NUL) { 3764 return NULL; 3765 } 3766 return rest; 3767 } 3768 3769 /// Check for syntax command option arguments. 3770 /// This can be called at any place in the list of arguments, and just picks 3771 /// out the arguments that are known. Can be called several times in a row to 3772 /// collect all options in between other arguments. 3773 /// 3774 /// @param arg next argument to be checked 3775 /// @param opt various things 3776 /// @param skip true if skipping over command 3777 /// 3778 /// @return a pointer to the next argument (which isn't an option). 3779 /// Return NULL for any error; 3780 static char *get_syn_options(char *arg, syn_opt_arg_T *opt, int *conceal_char, int skip) 3781 { 3782 int len = 0; 3783 int fidx; 3784 static const struct flag { 3785 char *name; 3786 int argtype; 3787 int flags; 3788 } flagtab[] = { { "cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED }, 3789 { "oOnNeElLiInNeE", 0, HL_ONELINE }, 3790 { "kKeEeEpPeEnNdD", 0, HL_KEEPEND }, 3791 { "eExXtTeEnNdD", 0, HL_EXTEND }, 3792 { "eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL }, 3793 { "tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP }, 3794 { "sSkKiIpPnNlL", 0, HL_SKIPNL }, 3795 { "sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE }, 3796 { "sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY }, 3797 { "gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE }, 3798 { "gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE }, 3799 { "dDiIsSpPlLaAyY", 0, HL_DISPLAY }, 3800 { "fFoOlLdD", 0, HL_FOLD }, 3801 { "cCoOnNcCeEaAlL", 0, HL_CONCEAL }, 3802 { "cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS }, 3803 { "cCcChHaArR", 11, 0 }, 3804 { "cCoOnNtTaAiInNsS", 1, 0 }, 3805 { "cCoOnNtTaAiInNeEdDiInN", 2, 0 }, 3806 { "nNeExXtTgGrRoOuUpP", 3, 0 }, }; 3807 static const char *const first_letters = "cCoOkKeEtTsSgGdDfFnN"; 3808 3809 if (arg == NULL) { // already detected error 3810 return NULL; 3811 } 3812 3813 if (curwin->w_s->b_syn_conceal) { 3814 opt->flags |= HL_CONCEAL; 3815 } 3816 3817 while (true) { 3818 // This is used very often when a large number of keywords is defined. 3819 // Need to skip quickly when no option name is found. 3820 // Also avoid tolower(), it's slow. 3821 if (strchr(first_letters, *arg) == NULL) { 3822 break; 3823 } 3824 3825 for (fidx = ARRAY_SIZE(flagtab); --fidx >= 0;) { 3826 char *p = flagtab[fidx].name; 3827 int i; 3828 for (i = 0, len = 0; p[i] != NUL; i += 2, len++) { 3829 if (arg[len] != p[i] && arg[len] != p[i + 1]) { 3830 break; 3831 } 3832 } 3833 if (p[i] == NUL && (ascii_iswhite(arg[len]) 3834 || (flagtab[fidx].argtype > 0 3835 ? arg[len] == '=' 3836 : ends_excmd(arg[len])))) { 3837 if (opt->keyword 3838 && (flagtab[fidx].flags == HL_DISPLAY 3839 || flagtab[fidx].flags == HL_FOLD 3840 || flagtab[fidx].flags == HL_EXTEND)) { 3841 // treat "display", "fold" and "extend" as a keyword 3842 fidx = -1; 3843 } 3844 break; 3845 } 3846 } 3847 if (fidx < 0) { // no match found 3848 break; 3849 } 3850 3851 if (flagtab[fidx].argtype == 1) { 3852 if (!opt->has_cont_list) { 3853 emsg(_(e_contains_argument_not_accepted_here)); 3854 return NULL; 3855 } 3856 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL) { 3857 return NULL; 3858 } 3859 } else if (flagtab[fidx].argtype == 2) { 3860 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL) { 3861 return NULL; 3862 } 3863 } else if (flagtab[fidx].argtype == 3) { 3864 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL) { 3865 return NULL; 3866 } 3867 } else if (flagtab[fidx].argtype == 11 && arg[5] == '=') { 3868 // cchar=? 3869 *conceal_char = utf_ptr2char(arg + 6); 3870 arg += utfc_ptr2len(arg + 6) - 1; 3871 if (!vim_isprintc(*conceal_char)) { 3872 emsg(_(e_invalid_cchar_value)); 3873 return NULL; 3874 } 3875 arg = skipwhite(arg + 7); 3876 } else { 3877 opt->flags |= flagtab[fidx].flags; 3878 arg = skipwhite(arg + len); 3879 3880 if (flagtab[fidx].flags == HL_SYNC_HERE 3881 || flagtab[fidx].flags == HL_SYNC_THERE) { 3882 if (opt->sync_idx == NULL) { 3883 emsg(_("E393: group[t]here not accepted here")); 3884 return NULL; 3885 } 3886 char *gname_start = arg; 3887 arg = skiptowhite(arg); 3888 if (gname_start == arg) { 3889 return NULL; 3890 } 3891 char *gname = xstrnsave(gname_start, (size_t)(arg - gname_start)); 3892 if (strcmp(gname, "NONE") == 0) { 3893 *opt->sync_idx = NONE_IDX; 3894 } else { 3895 int syn_id = syn_name2id(gname); 3896 int i; 3897 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0;) { 3898 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id 3899 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START) { 3900 *opt->sync_idx = i; 3901 break; 3902 } 3903 } 3904 if (i < 0) { 3905 semsg(_("E394: Didn't find region item for %s"), gname); 3906 xfree(gname); 3907 return NULL; 3908 } 3909 } 3910 3911 xfree(gname); 3912 arg = skipwhite(arg); 3913 } else if (flagtab[fidx].flags == HL_FOLD 3914 && foldmethodIsSyntax(curwin)) { 3915 // Need to update folds later. 3916 foldUpdateAll(curwin); 3917 } 3918 } 3919 } 3920 3921 return arg; 3922 } 3923 3924 // Adjustments to syntax item when declared in a ":syn include"'d file. 3925 // Set the contained flag, and if the item is not already contained, add it 3926 // to the specified top-level group, if any. 3927 static void syn_incl_toplevel(int id, int *flagsp) 3928 { 3929 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0) { 3930 return; 3931 } 3932 *flagsp |= HL_CONTAINED | HL_INCLUDED_TOPLEVEL; 3933 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER) { 3934 // We have to alloc this, because syn_combine_list() will free it. 3935 int16_t *grp_list = xmalloc(2 * sizeof(*grp_list)); 3936 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER; 3937 3938 grp_list[0] = (int16_t)id; 3939 grp_list[1] = 0; 3940 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list, 3941 CLUSTER_ADD); 3942 } 3943 } 3944 3945 // Handle ":syntax include [@{group-name}] filename" command. 3946 static void syn_cmd_include(exarg_T *eap, int syncing) 3947 { 3948 char *arg = eap->arg; 3949 int sgl_id = 1; 3950 char *group_name_end; 3951 const char *errormsg = NULL; 3952 bool source = false; 3953 3954 eap->nextcmd = find_nextcmd(arg); 3955 if (eap->skip) { 3956 return; 3957 } 3958 3959 if (arg[0] == '@') { 3960 arg++; 3961 char *rest = get_group_name(arg, &group_name_end); 3962 if (rest == NULL) { 3963 emsg(_("E397: Filename required")); 3964 return; 3965 } 3966 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg)); 3967 if (sgl_id == 0) { 3968 return; 3969 } 3970 // separate_nextcmd() and expand_filename() depend on this 3971 eap->arg = rest; 3972 } 3973 3974 // Everything that's left, up to the next command, should be the 3975 // filename to include. 3976 eap->argt |= (EX_XFILE | EX_NOSPC); 3977 separate_nextcmd(eap); 3978 if (*eap->arg == '<' || *eap->arg == '$' || path_is_absolute(eap->arg)) { 3979 // For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the 3980 // file. Need to expand the file name first. In other cases 3981 // ":runtime!" is used. 3982 source = true; 3983 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL) { 3984 if (errormsg != NULL) { 3985 emsg(errormsg); 3986 } 3987 return; 3988 } 3989 } 3990 3991 // Save and restore the existing top-level grouplist id and ":syn 3992 // include" tag around the actual inclusion. 3993 if (running_syn_inc_tag >= MAX_SYN_INC_TAG) { 3994 emsg(_("E847: Too many syntax includes")); 3995 return; 3996 } 3997 int prev_syn_inc_tag = current_syn_inc_tag; 3998 current_syn_inc_tag = ++running_syn_inc_tag; 3999 int prev_toplvl_grp = curwin->w_s->b_syn_topgrp; 4000 curwin->w_s->b_syn_topgrp = sgl_id; 4001 if (source 4002 ? do_source(eap->arg, false, DOSO_NONE, NULL) == FAIL 4003 : source_runtime(eap->arg, DIP_ALL) == FAIL) { 4004 semsg(_(e_notopen), eap->arg); 4005 } 4006 curwin->w_s->b_syn_topgrp = prev_toplvl_grp; 4007 current_syn_inc_tag = prev_syn_inc_tag; 4008 } 4009 4010 // Handle ":syntax keyword {group-name} [{option}] keyword .." command. 4011 static void syn_cmd_keyword(exarg_T *eap, int syncing) 4012 { 4013 char *arg = eap->arg; 4014 char *group_name_end; 4015 int syn_id; 4016 char *keyword_copy = NULL; 4017 syn_opt_arg_T syn_opt_arg; 4018 int conceal_char = NUL; 4019 4020 char *rest = get_group_name(arg, &group_name_end); 4021 4022 if (rest != NULL) { 4023 if (eap->skip) { 4024 syn_id = -1; 4025 } else { 4026 syn_id = syn_check_group(arg, (size_t)(group_name_end - arg)); 4027 } 4028 if (syn_id != 0) { 4029 // Allocate a buffer, for removing backslashes in the keyword. 4030 keyword_copy = xmalloc(strlen(rest) + 1); 4031 } 4032 if (keyword_copy != NULL) { 4033 syn_opt_arg.flags = 0; 4034 syn_opt_arg.keyword = true; 4035 syn_opt_arg.sync_idx = NULL; 4036 syn_opt_arg.has_cont_list = false; 4037 syn_opt_arg.cont_in_list = NULL; 4038 syn_opt_arg.next_list = NULL; 4039 4040 // The options given apply to ALL keywords, so all options must be 4041 // found before keywords can be created. 4042 // 1: collect the options and copy the keywords to keyword_copy. 4043 int cnt = 0; 4044 char *p = keyword_copy; 4045 for (; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest)) { 4046 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip); 4047 if (rest == NULL || ends_excmd(*rest)) { 4048 break; 4049 } 4050 // Copy the keyword, removing backslashes, and add a NUL. 4051 while (*rest != NUL && !ascii_iswhite(*rest)) { 4052 if (*rest == '\\' && rest[1] != NUL) { 4053 rest++; 4054 } 4055 *p++ = *rest++; 4056 } 4057 *p++ = NUL; 4058 cnt++; 4059 } 4060 4061 if (!eap->skip) { 4062 // Adjust flags for use of ":syn include". 4063 syn_incl_toplevel(syn_id, &syn_opt_arg.flags); 4064 4065 // 2: Add an entry for each keyword. 4066 size_t kwlen = 0; 4067 for (char *kw = keyword_copy; --cnt >= 0; kw += kwlen + 1) { 4068 for (p = vim_strchr(kw, '[');;) { 4069 if (p == NULL) { 4070 kwlen = strlen(kw); 4071 } else { 4072 *p = NUL; 4073 kwlen = (size_t)(p - kw); 4074 } 4075 add_keyword(kw, kwlen, syn_id, syn_opt_arg.flags, 4076 syn_opt_arg.cont_in_list, 4077 syn_opt_arg.next_list, conceal_char); 4078 if (p == NULL) { 4079 break; 4080 } 4081 if (p[1] == NUL) { 4082 semsg(_("E789: Missing ']': %s"), kw); 4083 goto error; 4084 } 4085 if (p[1] == ']') { 4086 if (p[2] != NUL) { 4087 semsg(_(e_trailing_char_after_rsb_str_str), kw, &p[2]); 4088 goto error; 4089 } 4090 kw = p + 1; 4091 kwlen = 1; 4092 break; // skip over the "]" 4093 } 4094 const int l = utfc_ptr2len(p + 1); 4095 4096 memmove(p, p + 1, (size_t)l); 4097 p += l; 4098 } 4099 } 4100 } 4101 4102 error: 4103 xfree(keyword_copy); 4104 xfree(syn_opt_arg.cont_in_list); 4105 xfree(syn_opt_arg.next_list); 4106 } 4107 } 4108 4109 if (rest != NULL) { 4110 eap->nextcmd = check_nextcmd(rest); 4111 } else { 4112 semsg(_(e_invarg2), arg); 4113 } 4114 4115 redraw_curbuf_later(UPD_SOME_VALID); 4116 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax. 4117 } 4118 4119 /// Handle ":syntax match {name} [{options}] {pattern} [{options}]". 4120 /// 4121 /// Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .." 4122 /// 4123 /// @param syncing true for ":syntax sync match .. " 4124 static void syn_cmd_match(exarg_T *eap, int syncing) 4125 { 4126 char *arg = eap->arg; 4127 char *group_name_end; 4128 synpat_T item; // the item found in the line 4129 int syn_id; 4130 syn_opt_arg_T syn_opt_arg; 4131 int sync_idx = 0; 4132 int conceal_char = NUL; 4133 4134 // Isolate the group name, check for validity 4135 char *rest = get_group_name(arg, &group_name_end); 4136 4137 // Get options before the pattern 4138 syn_opt_arg.flags = 0; 4139 syn_opt_arg.keyword = false; 4140 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL; 4141 syn_opt_arg.has_cont_list = true; 4142 syn_opt_arg.cont_list = NULL; 4143 syn_opt_arg.cont_in_list = NULL; 4144 syn_opt_arg.next_list = NULL; 4145 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip); 4146 4147 // get the pattern. 4148 init_syn_patterns(); 4149 CLEAR_FIELD(item); 4150 rest = get_syn_pattern(rest, &item); 4151 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL)) { 4152 syn_opt_arg.flags |= HL_HAS_EOL; 4153 } 4154 4155 // Get options after the pattern 4156 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip); 4157 4158 if (rest != NULL) { // all arguments are valid 4159 // Check for trailing command and illegal trailing arguments. 4160 eap->nextcmd = check_nextcmd(rest); 4161 if (!ends_excmd(*rest) || eap->skip) { 4162 rest = NULL; 4163 } else { 4164 if ((syn_id = syn_check_group(arg, (size_t)(group_name_end - arg))) != 0) { 4165 syn_incl_toplevel(syn_id, &syn_opt_arg.flags); 4166 // Store the pattern in the syn_items list 4167 synpat_T *spp = GA_APPEND_VIA_PTR(synpat_T, 4168 &curwin->w_s->b_syn_patterns); 4169 *spp = item; 4170 spp->sp_syncing = syncing; 4171 spp->sp_type = SPTYPE_MATCH; 4172 spp->sp_syn.id = (int16_t)syn_id; 4173 spp->sp_syn.inc_tag = current_syn_inc_tag; 4174 spp->sp_flags = syn_opt_arg.flags; 4175 spp->sp_sync_idx = sync_idx; 4176 spp->sp_cont_list = syn_opt_arg.cont_list; 4177 spp->sp_syn.cont_in_list = syn_opt_arg.cont_in_list; 4178 spp->sp_cchar = conceal_char; 4179 if (syn_opt_arg.cont_in_list != NULL) { 4180 curwin->w_s->b_syn_containedin = true; 4181 } 4182 spp->sp_next_list = syn_opt_arg.next_list; 4183 4184 // remember that we found a match for syncing on 4185 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE)) { 4186 curwin->w_s->b_syn_sync_flags |= SF_MATCH; 4187 } 4188 if (syn_opt_arg.flags & HL_FOLD) { 4189 curwin->w_s->b_syn_folditems++; 4190 } 4191 4192 redraw_curbuf_later(UPD_SOME_VALID); 4193 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax. 4194 return; // don't free the progs and patterns now 4195 } 4196 } 4197 } 4198 4199 // Something failed, free the allocated memory. 4200 vim_regfree(item.sp_prog); 4201 xfree(item.sp_pattern); 4202 xfree(syn_opt_arg.cont_list); 4203 xfree(syn_opt_arg.cont_in_list); 4204 xfree(syn_opt_arg.next_list); 4205 4206 if (rest == NULL) { 4207 semsg(_(e_invarg2), arg); 4208 } 4209 } 4210 4211 /// Handle ":syntax region {group-name} [matchgroup={group-name}] 4212 /// start {start} .. [skip {skip}] end {end} .. [{options}]". 4213 /// 4214 /// @param syncing true for ":syntax sync region .." 4215 static void syn_cmd_region(exarg_T *eap, int syncing) 4216 { 4217 char *arg = eap->arg; 4218 char *group_name_end; 4219 char *rest; // next arg, NULL on error 4220 char *key_end; 4221 char *key = NULL; 4222 int item; 4223 #define ITEM_START 0 4224 #define ITEM_SKIP 1 4225 #define ITEM_END 2 4226 #define ITEM_MATCHGROUP 3 4227 struct pat_ptr { 4228 synpat_T *pp_synp; // pointer to syn_pattern 4229 int pp_matchgroup_id; // matchgroup ID 4230 struct pat_ptr *pp_next; // pointer to next pat_ptr 4231 } *(pat_ptrs[3]); 4232 // patterns found in the line 4233 struct pat_ptr *ppp; 4234 struct pat_ptr *ppp_next; 4235 int pat_count = 0; // nr of syn_patterns found 4236 int syn_id; 4237 int matchgroup_id = 0; 4238 bool not_enough = false; // not enough arguments 4239 bool illegal = false; // illegal arguments 4240 bool success = false; 4241 syn_opt_arg_T syn_opt_arg; 4242 int conceal_char = NUL; 4243 4244 // Isolate the group name, check for validity 4245 rest = get_group_name(arg, &group_name_end); 4246 4247 pat_ptrs[0] = NULL; 4248 pat_ptrs[1] = NULL; 4249 pat_ptrs[2] = NULL; 4250 4251 init_syn_patterns(); 4252 4253 syn_opt_arg.flags = 0; 4254 syn_opt_arg.keyword = false; 4255 syn_opt_arg.sync_idx = NULL; 4256 syn_opt_arg.has_cont_list = true; 4257 syn_opt_arg.cont_list = NULL; 4258 syn_opt_arg.cont_in_list = NULL; 4259 syn_opt_arg.next_list = NULL; 4260 4261 // get the options, patterns and matchgroup. 4262 while (rest != NULL && !ends_excmd(*rest)) { 4263 // Check for option arguments 4264 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip); 4265 if (rest == NULL || ends_excmd(*rest)) { 4266 break; 4267 } 4268 4269 // must be a pattern or matchgroup then 4270 key_end = rest; 4271 while (*key_end && !ascii_iswhite(*key_end) && *key_end != '=') { 4272 key_end++; 4273 } 4274 xfree(key); 4275 key = vim_strnsave_up(rest, (size_t)(key_end - rest)); 4276 if (strcmp(key, "MATCHGROUP") == 0) { 4277 item = ITEM_MATCHGROUP; 4278 } else if (strcmp(key, "START") == 0) { 4279 item = ITEM_START; 4280 } else if (strcmp(key, "END") == 0) { 4281 item = ITEM_END; 4282 } else if (strcmp(key, "SKIP") == 0) { 4283 if (pat_ptrs[ITEM_SKIP] != NULL) { // One skip pattern allowed. 4284 illegal = true; 4285 break; 4286 } 4287 item = ITEM_SKIP; 4288 } else { 4289 break; 4290 } 4291 rest = skipwhite(key_end); 4292 if (*rest != '=') { 4293 rest = NULL; 4294 semsg(_("E398: Missing '=': %s"), arg); 4295 break; 4296 } 4297 rest = skipwhite(rest + 1); 4298 if (*rest == NUL) { 4299 not_enough = true; 4300 break; 4301 } 4302 4303 if (item == ITEM_MATCHGROUP) { 4304 char *p = skiptowhite(rest); 4305 if ((p - rest == 4 && strncmp(rest, "NONE", 4) == 0) || eap->skip) { 4306 matchgroup_id = 0; 4307 } else { 4308 matchgroup_id = syn_check_group(rest, (size_t)(p - rest)); 4309 if (matchgroup_id == 0) { 4310 illegal = true; 4311 break; 4312 } 4313 } 4314 rest = skipwhite(p); 4315 } else { 4316 // Allocate room for a syn_pattern, and link it in the list of 4317 // syn_patterns for this item, at the start (because the list is 4318 // used from end to start). 4319 ppp = xmalloc(sizeof(struct pat_ptr)); 4320 ppp->pp_next = pat_ptrs[item]; 4321 pat_ptrs[item] = ppp; 4322 ppp->pp_synp = xcalloc(1, sizeof(synpat_T)); 4323 4324 // Get the syntax pattern and the following offset(s). 4325 4326 // Enable the appropriate \z specials. 4327 if (item == ITEM_START) { 4328 reg_do_extmatch = REX_SET; 4329 } else { 4330 assert(item == ITEM_SKIP || item == ITEM_END); 4331 reg_do_extmatch = REX_USE; 4332 } 4333 rest = get_syn_pattern(rest, ppp->pp_synp); 4334 reg_do_extmatch = 0; 4335 if (item == ITEM_END && vim_regcomp_had_eol() 4336 && !(syn_opt_arg.flags & HL_EXCLUDENL)) { 4337 ppp->pp_synp->sp_flags |= HL_HAS_EOL; 4338 } 4339 ppp->pp_matchgroup_id = matchgroup_id; 4340 pat_count++; 4341 } 4342 } 4343 xfree(key); 4344 if (illegal || not_enough) { 4345 rest = NULL; 4346 } 4347 4348 // Must have a "start" and "end" pattern. 4349 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL 4350 || pat_ptrs[ITEM_END] == NULL)) { 4351 not_enough = true; 4352 rest = NULL; 4353 } 4354 4355 if (rest != NULL) { 4356 // Check for trailing garbage or command. 4357 // If OK, add the item. 4358 eap->nextcmd = check_nextcmd(rest); 4359 if (!ends_excmd(*rest) || eap->skip) { 4360 rest = NULL; 4361 } else { 4362 ga_grow(&(curwin->w_s->b_syn_patterns), pat_count); 4363 if ((syn_id = syn_check_group(arg, (size_t)(group_name_end - arg))) != 0) { 4364 syn_incl_toplevel(syn_id, &syn_opt_arg.flags); 4365 // Store the start/skip/end in the syn_items list 4366 int idx = curwin->w_s->b_syn_patterns.ga_len; 4367 for (item = ITEM_START; item <= ITEM_END; item++) { 4368 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next) { 4369 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp); 4370 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing; 4371 SYN_ITEMS(curwin->w_s)[idx].sp_type = 4372 (item == ITEM_START) ? SPTYPE_START 4373 : (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END; 4374 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags; 4375 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = (int16_t)syn_id; 4376 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = 4377 current_syn_inc_tag; 4378 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id = (int16_t)ppp->pp_matchgroup_id; 4379 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char; 4380 if (item == ITEM_START) { 4381 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = 4382 syn_opt_arg.cont_list; 4383 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list = 4384 syn_opt_arg.cont_in_list; 4385 if (syn_opt_arg.cont_in_list != NULL) { 4386 curwin->w_s->b_syn_containedin = true; 4387 } 4388 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = 4389 syn_opt_arg.next_list; 4390 } 4391 curwin->w_s->b_syn_patterns.ga_len++; 4392 idx++; 4393 if (syn_opt_arg.flags & HL_FOLD) { 4394 curwin->w_s->b_syn_folditems++; 4395 } 4396 } 4397 } 4398 4399 redraw_curbuf_later(UPD_SOME_VALID); 4400 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax. 4401 success = true; // don't free the progs and patterns now 4402 } 4403 } 4404 } 4405 4406 // Free the allocated memory. 4407 for (item = ITEM_START; item <= ITEM_END; item++) { 4408 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next) { 4409 if (!success && ppp->pp_synp != NULL) { 4410 vim_regfree(ppp->pp_synp->sp_prog); 4411 xfree(ppp->pp_synp->sp_pattern); 4412 } 4413 xfree(ppp->pp_synp); 4414 ppp_next = ppp->pp_next; 4415 xfree(ppp); 4416 } 4417 } 4418 4419 if (!success) { 4420 xfree(syn_opt_arg.cont_list); 4421 xfree(syn_opt_arg.cont_in_list); 4422 xfree(syn_opt_arg.next_list); 4423 if (not_enough) { 4424 semsg(_("E399: Not enough arguments: syntax region %s"), arg); 4425 } else if (illegal || rest == NULL) { 4426 semsg(_(e_invarg2), arg); 4427 } 4428 } 4429 } 4430 4431 // A simple syntax group ID comparison function suitable for use in qsort() 4432 static int syn_compare_stub(const void *const v1, const void *const v2) 4433 { 4434 const int16_t *const s1 = v1; 4435 const int16_t *const s2 = v2; 4436 4437 return *s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0; 4438 } 4439 4440 // Combines lists of syntax clusters. 4441 // *clstr1 and *clstr2 must both be allocated memory; they will be consumed. 4442 static void syn_combine_list(int16_t **const clstr1, int16_t **const clstr2, const int list_op) 4443 { 4444 size_t count1 = 0; 4445 size_t count2 = 0; 4446 const int16_t *g1; 4447 const int16_t *g2; 4448 int16_t *clstr = NULL; 4449 4450 // Handle degenerate cases. 4451 if (*clstr2 == NULL) { 4452 return; 4453 } 4454 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE) { 4455 if (list_op == CLUSTER_REPLACE) { 4456 xfree(*clstr1); 4457 } 4458 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD) { 4459 *clstr1 = *clstr2; 4460 } else { 4461 xfree(*clstr2); 4462 } 4463 return; 4464 } 4465 4466 for (g1 = *clstr1; *g1; g1++) { 4467 count1++; 4468 } 4469 for (g2 = *clstr2; *g2; g2++) { 4470 count2++; 4471 } 4472 4473 // For speed purposes, sort both lists. 4474 qsort(*clstr1, count1, sizeof(**clstr1), syn_compare_stub); 4475 qsort(*clstr2, count2, sizeof(**clstr2), syn_compare_stub); 4476 4477 // We proceed in two passes; in round 1, we count the elements to place 4478 // in the new list, and in round 2, we allocate and populate the new 4479 // list. For speed, we use a mergesort-like method, adding the smaller 4480 // of the current elements in each list to the new list. 4481 for (int round = 1; round <= 2; round++) { 4482 g1 = *clstr1; 4483 g2 = *clstr2; 4484 int count = 0; 4485 4486 // First, loop through the lists until one of them is empty. 4487 while (*g1 && *g2) { 4488 // We always want to add from the first list. 4489 if (*g1 < *g2) { 4490 if (round == 2) { 4491 clstr[count] = *g1; 4492 } 4493 count++; 4494 g1++; 4495 continue; 4496 } 4497 // We only want to add from the second list if we're adding the 4498 // lists. 4499 if (list_op == CLUSTER_ADD) { 4500 if (round == 2) { 4501 clstr[count] = *g2; 4502 } 4503 count++; 4504 } 4505 if (*g1 == *g2) { 4506 g1++; 4507 } 4508 g2++; 4509 } 4510 4511 // Now add the leftovers from whichever list didn't get finished 4512 // first. As before, we only want to add from the second list if 4513 // we're adding the lists. 4514 for (; *g1; g1++, count++) { 4515 if (round == 2) { 4516 clstr[count] = *g1; 4517 } 4518 } 4519 if (list_op == CLUSTER_ADD) { 4520 for (; *g2; g2++, count++) { 4521 if (round == 2) { 4522 clstr[count] = *g2; 4523 } 4524 } 4525 } 4526 4527 if (round == 1) { 4528 // If the group ended up empty, we don't need to allocate any 4529 // space for it. 4530 if (count == 0) { 4531 clstr = NULL; 4532 break; 4533 } 4534 clstr = xmalloc(((size_t)count + 1) * sizeof(*clstr)); 4535 clstr[count] = 0; 4536 } 4537 } 4538 4539 // Finally, put the new list in place. 4540 xfree(*clstr1); 4541 xfree(*clstr2); 4542 *clstr1 = clstr; 4543 } 4544 4545 /// Lookup a syntax cluster name and return its ID. 4546 /// If it is not found, 0 is returned. 4547 static int syn_scl_name2id(char *name) 4548 { 4549 // Avoid using stricmp() too much, it's slow on some systems 4550 char *name_u = vim_strsave_up(name); 4551 int i; 4552 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0;) { 4553 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL 4554 && strcmp(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0) { 4555 break; 4556 } 4557 } 4558 xfree(name_u); 4559 return i < 0 ? 0 : i + SYNID_CLUSTER; 4560 } 4561 4562 /// Like syn_scl_name2id(), but take a pointer + length argument. 4563 static int syn_scl_namen2id(char *linep, int len) 4564 { 4565 char *name = xstrnsave(linep, (size_t)len); 4566 int id = syn_scl_name2id(name); 4567 xfree(name); 4568 4569 return id; 4570 } 4571 4572 /// Find syntax cluster name in the table and return its ID. 4573 /// The argument is a pointer to the name and the length of the name. 4574 /// If it doesn't exist yet, a new entry is created. 4575 /// 4576 /// @return 0 for failure. 4577 static int syn_check_cluster(char *pp, int len) 4578 { 4579 char *name = xstrnsave(pp, (size_t)len); 4580 int id = syn_scl_name2id(name); 4581 if (id == 0) { // doesn't exist yet 4582 id = syn_add_cluster(name); 4583 } else { 4584 xfree(name); 4585 } 4586 return id; 4587 } 4588 4589 /// Add new syntax cluster and return its ID. 4590 /// "name" must be an allocated string, it will be consumed. 4591 /// 4592 /// @return 0 for failure. 4593 static int syn_add_cluster(char *name) 4594 { 4595 // First call for this growarray: init growing array. 4596 if (curwin->w_s->b_syn_clusters.ga_data == NULL) { 4597 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T); 4598 ga_set_growsize(&curwin->w_s->b_syn_clusters, 10); 4599 } 4600 4601 int len = curwin->w_s->b_syn_clusters.ga_len; 4602 if (len >= MAX_CLUSTER_ID) { 4603 emsg(_("E848: Too many syntax clusters")); 4604 xfree(name); 4605 return 0; 4606 } 4607 4608 syn_cluster_T *scp = GA_APPEND_VIA_PTR(syn_cluster_T, 4609 &curwin->w_s->b_syn_clusters); 4610 CLEAR_POINTER(scp); 4611 scp->scl_name = name; 4612 scp->scl_name_u = vim_strsave_up(name); 4613 scp->scl_list = NULL; 4614 4615 if (STRICMP(name, "Spell") == 0) { 4616 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER; 4617 } 4618 if (STRICMP(name, "NoSpell") == 0) { 4619 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER; 4620 } 4621 4622 return len + SYNID_CLUSTER; 4623 } 4624 4625 // Handle ":syntax cluster {cluster-name} [contains={groupname},..] 4626 // [add={groupname},..] [remove={groupname},..]". 4627 static void syn_cmd_cluster(exarg_T *eap, int syncing) 4628 { 4629 char *arg = eap->arg; 4630 char *group_name_end; 4631 bool got_clstr = false; 4632 int opt_len; 4633 int list_op; 4634 4635 eap->nextcmd = find_nextcmd(arg); 4636 if (eap->skip) { 4637 return; 4638 } 4639 4640 char *rest = get_group_name(arg, &group_name_end); 4641 4642 if (rest != NULL) { 4643 int scl_id = syn_check_cluster(arg, (int)(group_name_end - arg)); 4644 if (scl_id == 0) { 4645 return; 4646 } 4647 scl_id -= SYNID_CLUSTER; 4648 4649 while (true) { 4650 if (STRNICMP(rest, "add", 3) == 0 4651 && (ascii_iswhite(rest[3]) || rest[3] == '=')) { 4652 opt_len = 3; 4653 list_op = CLUSTER_ADD; 4654 } else if (STRNICMP(rest, "remove", 6) == 0 4655 && (ascii_iswhite(rest[6]) || rest[6] == '=')) { 4656 opt_len = 6; 4657 list_op = CLUSTER_SUBTRACT; 4658 } else if (STRNICMP(rest, "contains", 8) == 0 4659 && (ascii_iswhite(rest[8]) || rest[8] == '=')) { 4660 opt_len = 8; 4661 list_op = CLUSTER_REPLACE; 4662 } else { 4663 break; 4664 } 4665 4666 int16_t *clstr_list = NULL; 4667 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL) { 4668 semsg(_(e_invarg2), rest); 4669 break; 4670 } 4671 if (scl_id >= 0) { 4672 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list, 4673 &clstr_list, list_op); 4674 } else { 4675 xfree(clstr_list); 4676 } 4677 got_clstr = true; 4678 } 4679 4680 if (got_clstr) { 4681 redraw_curbuf_later(UPD_SOME_VALID); 4682 syn_stack_free_all(curwin->w_s); // Need to recompute all. 4683 } 4684 } 4685 4686 if (!got_clstr) { 4687 emsg(_("E400: No cluster specified")); 4688 } 4689 if (rest == NULL || !ends_excmd(*rest)) { 4690 semsg(_(e_invarg2), arg); 4691 } 4692 } 4693 4694 // On first call for current buffer: Init growing array. 4695 static void init_syn_patterns(void) 4696 { 4697 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T); 4698 ga_set_growsize(&curwin->w_s->b_syn_patterns, 10); 4699 } 4700 4701 /// Get one pattern for a ":syntax match" or ":syntax region" command. 4702 /// Stores the pattern and program in a synpat_T. 4703 /// 4704 /// @return a pointer to the next argument, or NULL in case of an error. 4705 static char *get_syn_pattern(char *arg, synpat_T *ci) 4706 { 4707 int idx; 4708 4709 // need at least three chars 4710 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL) { 4711 return NULL; 4712 } 4713 4714 char *end = skip_regexp(arg + 1, *arg, true); 4715 if (*end != *arg) { // end delimiter not found 4716 semsg(_("E401: Pattern delimiter not found: %s"), arg); 4717 return NULL; 4718 } 4719 // store the pattern and compiled regexp program 4720 ci->sp_pattern = xstrnsave(arg + 1, (size_t)(end - arg) - 1); 4721 4722 // Make 'cpoptions' empty, to avoid the 'l' flag 4723 char *cpo_save = p_cpo; 4724 p_cpo = empty_string_option; 4725 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC); 4726 p_cpo = cpo_save; 4727 4728 if (ci->sp_prog == NULL) { 4729 return NULL; 4730 } 4731 ci->sp_ic = curwin->w_s->b_syn_ic; 4732 syn_clear_time(&ci->sp_time); 4733 4734 // Check for a match, highlight or region offset. 4735 end++; 4736 do { 4737 for (idx = SPO_COUNT; --idx >= 0;) { 4738 if (strncmp(end, spo_name_tab[idx], 3) == 0) { 4739 break; 4740 } 4741 } 4742 if (idx >= 0) { 4743 int *p = &(ci->sp_offsets[idx]); 4744 if (idx != SPO_LC_OFF) { 4745 switch (end[3]) { 4746 case 's': 4747 break; 4748 case 'b': 4749 break; 4750 case 'e': 4751 idx += SPO_COUNT; break; 4752 default: 4753 idx = -1; break; 4754 } 4755 } 4756 if (idx >= 0) { 4757 ci->sp_off_flags |= (int16_t)(1 << idx); 4758 if (idx == SPO_LC_OFF) { // lc=99 4759 end += 3; 4760 *p = getdigits_int(&end, true, 0); 4761 4762 // "lc=" offset automatically sets "ms=" offset 4763 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF))) { 4764 ci->sp_off_flags |= (1 << SPO_MS_OFF); 4765 ci->sp_offsets[SPO_MS_OFF] = *p; 4766 } 4767 } else { // yy=x+99 4768 end += 4; 4769 if (*end == '+') { 4770 end++; 4771 *p = getdigits_int(&end, true, 0); // positive offset 4772 } else if (*end == '-') { 4773 end++; 4774 *p = -getdigits_int(&end, true, 0); // negative offset 4775 } 4776 } 4777 if (*end != ',') { 4778 break; 4779 } 4780 end++; 4781 } 4782 } 4783 } while (idx >= 0); 4784 4785 if (!ends_excmd(*end) && !ascii_iswhite(*end)) { 4786 semsg(_("E402: Garbage after pattern: %s"), arg); 4787 return NULL; 4788 } 4789 return skipwhite(end); 4790 } 4791 4792 /// Handle ":syntax sync .." command. 4793 static void syn_cmd_sync(exarg_T *eap, int syncing) 4794 { 4795 char *arg_start = eap->arg; 4796 char *key = NULL; 4797 bool illegal = false; 4798 bool finished = false; 4799 4800 if (ends_excmd(*arg_start)) { 4801 syn_cmd_list(eap, true); 4802 return; 4803 } 4804 4805 while (!ends_excmd(*arg_start)) { 4806 char *arg_end = skiptowhite(arg_start); 4807 char *next_arg = skipwhite(arg_end); 4808 xfree(key); 4809 key = vim_strnsave_up(arg_start, (size_t)(arg_end - arg_start)); 4810 if (strcmp(key, "CCOMMENT") == 0) { 4811 if (!eap->skip) { 4812 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT; 4813 } 4814 if (!ends_excmd(*next_arg)) { 4815 arg_end = skiptowhite(next_arg); 4816 if (!eap->skip) { 4817 curwin->w_s->b_syn_sync_id = 4818 (int16_t)syn_check_group(next_arg, (size_t)(arg_end - next_arg)); 4819 } 4820 next_arg = skipwhite(arg_end); 4821 } else if (!eap->skip) { 4822 curwin->w_s->b_syn_sync_id = (int16_t)syn_name2id("Comment"); 4823 } 4824 } else if (strncmp(key, "LINES", 5) == 0 4825 || strncmp(key, "MINLINES", 8) == 0 4826 || strncmp(key, "MAXLINES", 8) == 0 4827 || strncmp(key, "LINEBREAKS", 10) == 0) { 4828 if (key[4] == 'S') { 4829 arg_end = key + 6; 4830 } else if (key[0] == 'L') { 4831 arg_end = key + 11; 4832 } else { 4833 arg_end = key + 9; 4834 } 4835 if (arg_end[-1] != '=' || !ascii_isdigit(*arg_end)) { 4836 illegal = true; 4837 break; 4838 } 4839 linenr_T n = getdigits_int32(&arg_end, false, 0); 4840 if (!eap->skip) { 4841 if (key[4] == 'B') { 4842 curwin->w_s->b_syn_sync_linebreaks = n; 4843 } else if (key[1] == 'A') { 4844 curwin->w_s->b_syn_sync_maxlines = n; 4845 } else { 4846 curwin->w_s->b_syn_sync_minlines = n; 4847 } 4848 } 4849 } else if (strcmp(key, "FROMSTART") == 0) { 4850 if (!eap->skip) { 4851 curwin->w_s->b_syn_sync_minlines = MAXLNUM; 4852 curwin->w_s->b_syn_sync_maxlines = 0; 4853 } 4854 } else if (strcmp(key, "LINECONT") == 0) { 4855 if (*next_arg == NUL) { // missing pattern 4856 illegal = true; 4857 break; 4858 } 4859 if (curwin->w_s->b_syn_linecont_pat != NULL) { 4860 emsg(_("E403: syntax sync: line continuations pattern specified twice")); 4861 finished = true; 4862 break; 4863 } 4864 arg_end = skip_regexp(next_arg + 1, *next_arg, true); 4865 if (*arg_end != *next_arg) { // end delimiter not found 4866 illegal = true; 4867 break; 4868 } 4869 4870 if (!eap->skip) { 4871 // store the pattern and compiled regexp program 4872 curwin->w_s->b_syn_linecont_pat = 4873 xstrnsave(next_arg + 1, (size_t)(arg_end - next_arg) - 1); 4874 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic; 4875 4876 // Make 'cpoptions' empty, to avoid the 'l' flag 4877 char *cpo_save = p_cpo; 4878 p_cpo = empty_string_option; 4879 curwin->w_s->b_syn_linecont_prog = 4880 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC); 4881 p_cpo = cpo_save; 4882 syn_clear_time(&curwin->w_s->b_syn_linecont_time); 4883 4884 if (curwin->w_s->b_syn_linecont_prog == NULL) { 4885 XFREE_CLEAR(curwin->w_s->b_syn_linecont_pat); 4886 finished = true; 4887 break; 4888 } 4889 } 4890 next_arg = skipwhite(arg_end + 1); 4891 } else { 4892 eap->arg = next_arg; 4893 if (strcmp(key, "MATCH") == 0) { 4894 syn_cmd_match(eap, true); 4895 } else if (strcmp(key, "REGION") == 0) { 4896 syn_cmd_region(eap, true); 4897 } else if (strcmp(key, "CLEAR") == 0) { 4898 syn_cmd_clear(eap, true); 4899 } else { 4900 illegal = true; 4901 } 4902 finished = true; 4903 break; 4904 } 4905 arg_start = next_arg; 4906 } 4907 xfree(key); 4908 if (illegal) { 4909 semsg(_("E404: Illegal arguments: %s"), arg_start); 4910 } else if (!finished) { 4911 eap->nextcmd = check_nextcmd(arg_start); 4912 redraw_curbuf_later(UPD_SOME_VALID); 4913 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax. 4914 } 4915 } 4916 4917 /// Convert a line of highlight group names into a list of group ID numbers. 4918 /// "arg" should point to the "contains" or "nextgroup" keyword. 4919 /// "arg" is advanced to after the last group name. 4920 /// Careful: the argument is modified (NULs added). 4921 /// 4922 /// @param keylen length of keyword 4923 /// @param list where to store the resulting list, if not NULL, the list is silently skipped! 4924 /// 4925 /// @return FAIL for some error, OK for success. 4926 static int get_id_list(char **const arg, const int keylen, int16_t **const list, const bool skip) 4927 { 4928 char *p = NULL; 4929 char *end; 4930 int total_count = 0; 4931 int16_t *retval = NULL; 4932 regmatch_T regmatch; 4933 int id; 4934 bool failed = false; 4935 4936 // We parse the list twice: 4937 // round == 1: count the number of items, allocate the array. 4938 // round == 2: fill the array with the items. 4939 // In round 1 new groups may be added, causing the number of items to 4940 // grow when a regexp is used. In that case round 1 is done once again. 4941 for (int round = 1; round <= 2; round++) { 4942 // skip "contains" 4943 p = skipwhite(*arg + keylen); 4944 if (*p != '=') { 4945 semsg(_("E405: Missing equal sign: %s"), *arg); 4946 break; 4947 } 4948 p = skipwhite(p + 1); 4949 if (ends_excmd(*p)) { 4950 semsg(_("E406: Empty argument: %s"), *arg); 4951 break; 4952 } 4953 4954 // parse the arguments after "contains" 4955 int count = 0; 4956 do { 4957 for (end = p; *end && !ascii_iswhite(*end) && *end != ','; end++) {} 4958 char *const name = xmalloc((size_t)(end - p) + 3); // leave room for "^$" 4959 xmemcpyz(name + 1, p, (size_t)(end - p)); 4960 if (strcmp(name + 1, "ALLBUT") == 0 4961 || strcmp(name + 1, "ALL") == 0 4962 || strcmp(name + 1, "TOP") == 0 4963 || strcmp(name + 1, "CONTAINED") == 0) { 4964 if (TOUPPER_ASC(**arg) != 'C') { 4965 semsg(_("E407: %s not allowed here"), name + 1); 4966 failed = true; 4967 xfree(name); 4968 break; 4969 } 4970 if (count != 0) { 4971 semsg(_("E408: %s must be first in contains list"), 4972 name + 1); 4973 failed = true; 4974 xfree(name); 4975 break; 4976 } 4977 if (name[1] == 'A') { 4978 id = SYNID_ALLBUT; 4979 } else if (name[1] == 'T') { 4980 id = SYNID_TOP; 4981 } else { 4982 id = SYNID_CONTAINED; 4983 } 4984 id += current_syn_inc_tag; 4985 } else if (name[1] == '@') { 4986 if (skip) { 4987 id = -1; 4988 } else { 4989 id = syn_check_cluster(name + 2, (int)(end - p - 1)); 4990 } 4991 } else { 4992 // Handle full group name. 4993 if (strpbrk(name + 1, "\\.*^$~[") == NULL) { 4994 id = syn_check_group((name + 1), (size_t)(end - p)); 4995 } else { 4996 // Handle match of regexp with group names. 4997 *name = '^'; 4998 strcat(name, "$"); 4999 regmatch.regprog = vim_regcomp(name, RE_MAGIC); 5000 if (regmatch.regprog == NULL) { 5001 failed = true; 5002 xfree(name); 5003 break; 5004 } 5005 5006 regmatch.rm_ic = true; 5007 id = 0; 5008 for (int i = highlight_num_groups(); --i >= 0;) { 5009 if (vim_regexec(®match, highlight_group_name(i), 0)) { 5010 if (round == 2) { 5011 // Got more items than expected; can happen 5012 // when adding items that match: 5013 // "contains=a.*b,axb". 5014 // Go back to first round. 5015 if (count >= total_count) { 5016 xfree(retval); 5017 round = 1; 5018 } else { 5019 retval[count] = (int16_t)(i + 1); 5020 } 5021 } 5022 count++; 5023 id = -1; // Remember that we found one. 5024 } 5025 } 5026 vim_regfree(regmatch.regprog); 5027 } 5028 } 5029 xfree(name); 5030 if (id == 0) { 5031 semsg(_("E409: Unknown group name: %s"), p); 5032 failed = true; 5033 break; 5034 } 5035 if (id > 0) { 5036 if (round == 2) { 5037 // Got more items than expected, go back to first round. 5038 if (count >= total_count) { 5039 xfree(retval); 5040 round = 1; 5041 } else { 5042 retval[count] = (int16_t)id; 5043 } 5044 } 5045 count++; 5046 } 5047 p = skipwhite(end); 5048 if (*p != ',') { 5049 break; 5050 } 5051 p = skipwhite(p + 1); // skip comma in between arguments 5052 } while (!ends_excmd(*p)); 5053 if (failed) { 5054 break; 5055 } 5056 if (round == 1) { 5057 retval = xmalloc(((size_t)count + 1) * sizeof(*retval)); 5058 retval[count] = 0; // zero means end of the list 5059 total_count = count; 5060 } 5061 } 5062 5063 *arg = p; 5064 if (failed || retval == NULL) { 5065 xfree(retval); 5066 return FAIL; 5067 } 5068 5069 if (*list == NULL) { 5070 *list = retval; 5071 } else { 5072 xfree(retval); // list already found, don't overwrite it 5073 } 5074 return OK; 5075 } 5076 5077 // Make a copy of an ID list. 5078 static int16_t *copy_id_list(const int16_t *const list) 5079 { 5080 if (list == NULL) { 5081 return NULL; 5082 } 5083 5084 int count; 5085 for (count = 0; list[count]; count++) {} 5086 const size_t len = ((size_t)count + 1) * sizeof(int16_t); 5087 int16_t *const retval = xmalloc(len); 5088 memmove(retval, list, len); 5089 5090 return retval; 5091 } 5092 5093 /// Check if syntax group "ssp" is in the ID list "list" of "cur_si". 5094 /// "cur_si" can be NULL if not checking the "containedin" list. 5095 /// Used to check if a syntax item is in the "contains" or "nextgroup" list of 5096 /// the current item. 5097 /// This function is called very often, keep it fast!! 5098 /// 5099 /// @param cur_si current item or NULL 5100 /// @param list id list 5101 /// @param ssp group id and ":syn include" tag of group 5102 /// @param flags group flags 5103 static int in_id_list(stateitem_T *cur_si, int16_t *list, struct sp_syn *ssp, int flags) 5104 { 5105 int retval; 5106 int16_t id = ssp->id; 5107 static int depth = 0; 5108 5109 // If ssp has a "containedin" list and "cur_si" is in it, return true. 5110 if (cur_si != NULL && ssp->cont_in_list != NULL 5111 && !(cur_si->si_flags & HL_MATCH)) { 5112 // Ignore transparent items without a contains argument. Double check 5113 // that we don't go back past the first one. 5114 while ((cur_si->si_flags & HL_TRANS_CONT) 5115 && cur_si > (stateitem_T *)(current_state.ga_data)) { 5116 cur_si--; 5117 } 5118 // cur_si->si_idx is -1 for keywords, these never contain anything. 5119 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list, 5120 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn), 5121 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags)) { 5122 return true; 5123 } 5124 } 5125 5126 if (list == NULL) { 5127 return false; 5128 } 5129 5130 // If list is ID_LIST_ALL, we are in a transparent item that isn't 5131 // inside anything. Only allow not-contained groups. 5132 if (list == ID_LIST_ALL) { 5133 return !(flags & HL_CONTAINED); 5134 } 5135 5136 // Is this top-level (i.e. not 'contained') in the file it was declared in? 5137 // For included files, this is different from HL_CONTAINED, which is set 5138 // unconditionally. 5139 bool toplevel = !(flags & HL_CONTAINED) || (flags & HL_INCLUDED_TOPLEVEL); 5140 5141 // If the first item is "ALLBUT", return true if "id" is NOT in the 5142 // contains list. We also require that "id" is at the same ":syn include" 5143 // level as the list. 5144 int16_t item = *list; 5145 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER) { 5146 if (item < SYNID_TOP) { 5147 // ALL or ALLBUT: accept all groups in the same file 5148 if (item - SYNID_ALLBUT != ssp->inc_tag) { 5149 return false; 5150 } 5151 } else if (item < SYNID_CONTAINED) { 5152 // TOP: accept all not-contained groups in the same file 5153 if (item - SYNID_TOP != ssp->inc_tag || !toplevel) { 5154 return false; 5155 } 5156 } else { 5157 // CONTAINED: accept all contained groups in the same file 5158 if (item - SYNID_CONTAINED != ssp->inc_tag || toplevel) { 5159 return false; 5160 } 5161 } 5162 item = *++list; 5163 retval = false; 5164 } else { 5165 retval = true; 5166 } 5167 5168 // Return "retval" if id is in the contains list. 5169 while (item != 0) { 5170 if (item == id) { 5171 return retval; 5172 } 5173 if (item >= SYNID_CLUSTER) { 5174 int16_t *scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list; 5175 // restrict recursiveness to 30 to avoid an endless loop for a 5176 // cluster that includes itself (indirectly) 5177 if (scl_list != NULL && depth < 30) { 5178 depth++; 5179 int r = in_id_list(NULL, scl_list, ssp, flags); 5180 depth--; 5181 if (r) { 5182 return retval; 5183 } 5184 } 5185 } 5186 item = *++list; 5187 } 5188 return !retval; 5189 } 5190 5191 struct subcommand { 5192 char *name; // subcommand name 5193 void (*func)(exarg_T *, int); // function to call 5194 }; 5195 5196 static struct subcommand subcommands[] = { 5197 { "case", syn_cmd_case }, 5198 { "clear", syn_cmd_clear }, 5199 { "cluster", syn_cmd_cluster }, 5200 { "conceal", syn_cmd_conceal }, 5201 { "enable", syn_cmd_on }, 5202 { "foldlevel", syn_cmd_foldlevel }, 5203 { "include", syn_cmd_include }, 5204 { "iskeyword", syn_cmd_iskeyword }, 5205 { "keyword", syn_cmd_keyword }, 5206 { "list", syn_cmd_list }, 5207 { "manual", syn_cmd_manual }, 5208 { "match", syn_cmd_match }, 5209 { "on", syn_cmd_on }, 5210 { "off", syn_cmd_off }, 5211 { "region", syn_cmd_region }, 5212 { "reset", syn_cmd_reset }, 5213 { "spell", syn_cmd_spell }, 5214 { "sync", syn_cmd_sync }, 5215 { "", syn_cmd_list }, 5216 }; 5217 5218 /// ":syntax". 5219 /// This searches the subcommands[] table for the subcommand name, and calls a 5220 /// syntax_subcommand() function to do the rest. 5221 void ex_syntax(exarg_T *eap) 5222 { 5223 char *arg = eap->arg; 5224 char *subcmd_end; 5225 5226 syn_cmdlinep = eap->cmdlinep; 5227 5228 // isolate subcommand name 5229 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); subcmd_end++) {} 5230 char *const subcmd_name = xstrnsave(arg, (size_t)(subcmd_end - arg)); 5231 if (eap->skip) { // skip error messages for all subcommands 5232 emsg_skip++; 5233 } 5234 size_t i; 5235 for (i = 0; i < ARRAY_SIZE(subcommands); i++) { 5236 if (strcmp(subcmd_name, subcommands[i].name) == 0) { 5237 eap->arg = skipwhite(subcmd_end); 5238 (subcommands[i].func)(eap, false); 5239 break; 5240 } 5241 } 5242 5243 if (i == ARRAY_SIZE(subcommands)) { 5244 semsg(_("E410: Invalid :syntax subcommand: %s"), subcmd_name); 5245 } 5246 5247 xfree(subcmd_name); 5248 if (eap->skip) { 5249 emsg_skip--; 5250 } 5251 } 5252 5253 /// @deprecated 5254 void ex_ownsyntax(exarg_T *eap) 5255 { 5256 if (curwin->w_s == &curwin->w_buffer->b_s) { 5257 curwin->w_s = xcalloc(1, sizeof(synblock_T)); 5258 hash_init(&curwin->w_s->b_keywtab); 5259 hash_init(&curwin->w_s->b_keywtab_ic); 5260 // TODO(vim): Keep the spell checking as it was. 5261 curwin->w_p_spell = false; // No spell checking 5262 // make sure option values are "empty_string_option" instead of NULL 5263 clear_string_option(&curwin->w_s->b_p_spc); 5264 clear_string_option(&curwin->w_s->b_p_spf); 5265 clear_string_option(&curwin->w_s->b_p_spl); 5266 clear_string_option(&curwin->w_s->b_p_spo); 5267 clear_string_option(&curwin->w_s->b_syn_isk); 5268 } 5269 5270 // Save value of b:current_syntax. 5271 char *old_value = get_var_value("b:current_syntax"); 5272 if (old_value != NULL) { 5273 old_value = xstrdup(old_value); 5274 } 5275 5276 // Apply the "syntax" autocommand event, this finds and loads the syntax file. 5277 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, true, curbuf); 5278 5279 // Move value of b:current_syntax to w:current_syntax. 5280 char *new_value = get_var_value("b:current_syntax"); 5281 if (new_value != NULL) { 5282 set_internal_string_var("w:current_syntax", new_value); 5283 } 5284 5285 // Restore value of b:current_syntax. 5286 if (old_value == NULL) { 5287 do_unlet(S_LEN("b:current_syntax"), true); 5288 } else { 5289 set_internal_string_var("b:current_syntax", old_value); 5290 xfree(old_value); 5291 } 5292 } 5293 5294 bool syntax_present(win_T *win) 5295 { 5296 return win->w_s->b_syn_patterns.ga_len != 0 5297 || win->w_s->b_syn_clusters.ga_len != 0 5298 || win->w_s->b_keywtab.ht_used > 0 5299 || win->w_s->b_keywtab_ic.ht_used > 0; 5300 } 5301 5302 static enum { 5303 EXP_SUBCMD, // expand ":syn" sub-commands 5304 EXP_CASE, // expand ":syn case" arguments 5305 EXP_SPELL, // expand ":syn spell" arguments 5306 EXP_SYNC, // expand ":syn sync" arguments 5307 EXP_CLUSTER, // expand ":syn list @cluster" arguments 5308 } expand_what; 5309 5310 // Reset include_link, include_default, include_none to 0. 5311 // Called when we are done expanding. 5312 void reset_expand_highlight(void) 5313 { 5314 include_link = include_default = include_none = 0; 5315 } 5316 5317 // Handle command line completion for :match and :echohl command: Add "None" 5318 // as highlight group. 5319 void set_context_in_echohl_cmd(expand_T *xp, const char *arg) 5320 { 5321 xp->xp_context = EXPAND_HIGHLIGHT; 5322 xp->xp_pattern = (char *)arg; 5323 include_none = 1; 5324 } 5325 5326 // Handle command line completion for :syntax command. 5327 void set_context_in_syntax_cmd(expand_T *xp, const char *arg) 5328 { 5329 // Default: expand subcommands. 5330 xp->xp_context = EXPAND_SYNTAX; 5331 expand_what = EXP_SUBCMD; 5332 xp->xp_pattern = (char *)arg; 5333 include_link = 0; 5334 include_default = 0; 5335 5336 if (*arg == NUL) { 5337 return; 5338 } 5339 5340 // (part of) subcommand already typed 5341 const char *p = skiptowhite(arg); 5342 if (*p == NUL) { 5343 return; 5344 } 5345 5346 // past first world 5347 xp->xp_pattern = skipwhite(p); 5348 if (*skiptowhite(xp->xp_pattern) != NUL) { 5349 xp->xp_context = EXPAND_NOTHING; 5350 } else if (STRNICMP(arg, "case", p - arg) == 0) { 5351 expand_what = EXP_CASE; 5352 } else if (STRNICMP(arg, "spell", p - arg) == 0) { 5353 expand_what = EXP_SPELL; 5354 } else if (STRNICMP(arg, "sync", p - arg) == 0) { 5355 expand_what = EXP_SYNC; 5356 } else if (STRNICMP(arg, "list", p - arg) == 0) { 5357 p = skipwhite(p); 5358 if (*p == '@') { 5359 expand_what = EXP_CLUSTER; 5360 } else { 5361 xp->xp_context = EXPAND_HIGHLIGHT; 5362 } 5363 } else if (STRNICMP(arg, "keyword", p - arg) == 0 5364 || STRNICMP(arg, "region", p - arg) == 0 5365 || STRNICMP(arg, "match", p - arg) == 0) { 5366 xp->xp_context = EXPAND_HIGHLIGHT; 5367 } else { 5368 xp->xp_context = EXPAND_NOTHING; 5369 } 5370 } 5371 5372 // Function given to ExpandGeneric() to obtain the list syntax names for 5373 // expansion. 5374 char *get_syntax_name(expand_T *xp, int idx) 5375 { 5376 switch (expand_what) { 5377 case EXP_SUBCMD: 5378 if (idx < 0 || idx >= (int)ARRAY_SIZE(subcommands)) { 5379 return NULL; 5380 } 5381 return subcommands[idx].name; 5382 case EXP_CASE: { 5383 static char *case_args[] = { "match", "ignore", NULL }; 5384 return case_args[idx]; 5385 } 5386 case EXP_SPELL: { 5387 static char *spell_args[] = 5388 { "toplevel", "notoplevel", "default", NULL }; 5389 return spell_args[idx]; 5390 } 5391 case EXP_SYNC: { 5392 static char *sync_args[] = 5393 { "ccomment", "clear", "fromstart", 5394 "linebreaks=", "linecont", "lines=", "match", 5395 "maxlines=", "minlines=", "region", NULL }; 5396 return sync_args[idx]; 5397 } 5398 case EXP_CLUSTER: 5399 if (idx < curwin->w_s->b_syn_clusters.ga_len) { 5400 vim_snprintf(xp->xp_buf, EXPAND_BUF_LEN, "@%s", 5401 SYN_CLSTR(curwin->w_s)[idx].scl_name); 5402 return xp->xp_buf; 5403 } else { 5404 return NULL; 5405 } 5406 } 5407 return NULL; 5408 } 5409 5410 /// Function called for expression evaluation: get syntax ID at file position. 5411 /// 5412 /// @param trans remove transparency 5413 /// @param spellp return: can do spell checking 5414 /// @param keep_state keep state of char at "col" 5415 int syn_get_id(win_T *wp, linenr_T lnum, colnr_T col, int trans, bool *spellp, int keep_state) 5416 { 5417 // When the position is not after the current position and in the same 5418 // line of the same window with the same buffer, need to restart parsing. 5419 if (wp != syn_win || wp->w_buffer != syn_buf || lnum != current_lnum || col < current_col) { 5420 syntax_start(wp, lnum); 5421 } else if (col > current_col) { 5422 // next_match may not be correct when moving around, e.g. with the 5423 // "skip" expression in searchpair() 5424 next_match_idx = -1; 5425 } 5426 5427 get_syntax_attr(col, spellp, keep_state); 5428 5429 return trans ? current_trans_id : current_id; 5430 } 5431 5432 // Get extra information about the syntax item. Must be called right after 5433 // get_syntax_attr(). 5434 // Stores the current item sequence nr in "*seqnrp". 5435 // Returns the current flags. 5436 int get_syntax_info(int *seqnrp) 5437 { 5438 *seqnrp = current_seqnr; 5439 return current_flags; 5440 } 5441 5442 /// Get the sequence number of the concealed file position. 5443 /// 5444 /// @return seqnr if the file position is concealed, 0 otherwise. 5445 int syn_get_concealed_id(win_T *wp, linenr_T lnum, colnr_T col) 5446 { 5447 int seqnr; 5448 5449 syn_get_id(wp, lnum, col, false, NULL, false); 5450 int syntax_flags = get_syntax_info(&seqnr); 5451 5452 if (syntax_flags & HL_CONCEAL) { 5453 return seqnr; 5454 } 5455 return 0; 5456 } 5457 5458 // Return conceal substitution character 5459 int syn_get_sub_char(void) 5460 { 5461 return current_sub_char; 5462 } 5463 5464 // Return the syntax ID at position "i" in the current stack. 5465 // The caller must have called syn_get_id() before to fill the stack. 5466 // Returns -1 when "i" is out of range. 5467 int syn_get_stack_item(int i) 5468 { 5469 if (i >= current_state.ga_len) { 5470 // Need to invalidate the state, because we didn't properly finish it 5471 // for the last character, "keep_state" was true. 5472 invalidate_current_state(); 5473 current_col = MAXCOL; 5474 return -1; 5475 } 5476 return CUR_STATE(i).si_id; 5477 } 5478 5479 static int syn_cur_foldlevel(void) 5480 { 5481 int level = 0; 5482 for (int i = 0; i < current_state.ga_len; i++) { 5483 if (CUR_STATE(i).si_flags & HL_FOLD) { 5484 level++; 5485 } 5486 } 5487 return level; 5488 } 5489 5490 /// Function called to get folding level for line "lnum" in window "wp". 5491 int syn_get_foldlevel(win_T *wp, linenr_T lnum) 5492 { 5493 int level = 0; 5494 5495 // Return quickly when there are no fold items at all. 5496 if (wp->w_s->b_syn_folditems != 0 5497 && !wp->w_s->b_syn_error 5498 && !wp->w_s->b_syn_slow) { 5499 syntax_start(wp, lnum); 5500 5501 // Start with the fold level at the start of the line. 5502 level = syn_cur_foldlevel(); 5503 5504 if (wp->w_s->b_syn_foldlevel == SYNFLD_MINIMUM) { 5505 // Find the lowest fold level that is followed by a higher one. 5506 int cur_level = level; 5507 int low_level = cur_level; 5508 while (!current_finished) { 5509 syn_current_attr(false, false, NULL, false); 5510 cur_level = syn_cur_foldlevel(); 5511 if (cur_level < low_level) { 5512 low_level = cur_level; 5513 } else if (cur_level > low_level) { 5514 level = low_level; 5515 } 5516 current_col++; 5517 } 5518 } 5519 } 5520 if (level > wp->w_p_fdn) { 5521 level = (int)wp->w_p_fdn; 5522 if (level < 0) { 5523 level = 0; 5524 } 5525 } 5526 return level; 5527 } 5528 5529 // ":syntime". 5530 void ex_syntime(exarg_T *eap) 5531 { 5532 if (strcmp(eap->arg, "on") == 0) { 5533 syn_time_on = true; 5534 } else if (strcmp(eap->arg, "off") == 0) { 5535 syn_time_on = false; 5536 } else if (strcmp(eap->arg, "clear") == 0) { 5537 syntime_clear(); 5538 } else if (strcmp(eap->arg, "report") == 0) { 5539 syntime_report(); 5540 } else { 5541 semsg(_(e_invarg2), eap->arg); 5542 } 5543 } 5544 5545 static void syn_clear_time(syn_time_T *st) 5546 { 5547 st->total = profile_zero(); 5548 st->slowest = profile_zero(); 5549 st->count = 0; 5550 st->match = 0; 5551 } 5552 5553 // Clear the syntax timing for the current buffer. 5554 static void syntime_clear(void) 5555 { 5556 synpat_T *spp; 5557 5558 if (!syntax_present(curwin)) { 5559 msg(_(msg_no_items), 0); 5560 return; 5561 } 5562 for (int idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; idx++) { 5563 spp = &(SYN_ITEMS(curwin->w_s)[idx]); 5564 syn_clear_time(&spp->sp_time); 5565 } 5566 } 5567 5568 // Function given to ExpandGeneric() to obtain the possible arguments of the 5569 // ":syntime {on,off,clear,report}" command. 5570 char *get_syntime_arg(expand_T *xp, int idx) 5571 { 5572 switch (idx) { 5573 case 0: 5574 return "on"; 5575 case 1: 5576 return "off"; 5577 case 2: 5578 return "clear"; 5579 case 3: 5580 return "report"; 5581 } 5582 return NULL; 5583 } 5584 5585 static int syn_compare_syntime(const void *v1, const void *v2) 5586 { 5587 const time_entry_T *s1 = v1; 5588 const time_entry_T *s2 = v2; 5589 5590 return profile_cmp(s1->total, s2->total); 5591 } 5592 5593 // Clear the syntax timing for the current buffer. 5594 static void syntime_report(void) 5595 { 5596 if (!syntax_present(curwin)) { 5597 msg(_(msg_no_items), 0); 5598 return; 5599 } 5600 5601 garray_T ga; 5602 ga_init(&ga, sizeof(time_entry_T), 50); 5603 5604 proftime_T total_total = profile_zero(); 5605 int total_count = 0; 5606 time_entry_T *p; 5607 for (int idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; idx++) { 5608 synpat_T *spp = &(SYN_ITEMS(curwin->w_s)[idx]); 5609 if (spp->sp_time.count > 0) { 5610 p = GA_APPEND_VIA_PTR(time_entry_T, &ga); 5611 p->total = spp->sp_time.total; 5612 total_total = profile_add(total_total, spp->sp_time.total); 5613 p->count = spp->sp_time.count; 5614 p->match = spp->sp_time.match; 5615 total_count += spp->sp_time.count; 5616 p->slowest = spp->sp_time.slowest; 5617 proftime_T tm = profile_divide(spp->sp_time.total, spp->sp_time.count); 5618 p->average = tm; 5619 p->id = spp->sp_syn.id; 5620 p->pattern = spp->sp_pattern; 5621 } 5622 } 5623 5624 // Sort on total time. Skip if there are no items to avoid passing NULL 5625 // pointer to qsort(). 5626 if (ga.ga_len > 1) { 5627 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T), 5628 syn_compare_syntime); 5629 } 5630 5631 msg_puts_title(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN")); 5632 msg_puts("\n"); 5633 for (int idx = 0; idx < ga.ga_len && !got_int; idx++) { 5634 p = ((time_entry_T *)ga.ga_data) + idx; 5635 5636 msg_puts(profile_msg(p->total)); 5637 msg_puts(" "); // make sure there is always a separating space 5638 msg_advance(13); 5639 msg_outnum(p->count); 5640 msg_puts(" "); 5641 msg_advance(20); 5642 msg_outnum(p->match); 5643 msg_puts(" "); 5644 msg_advance(26); 5645 msg_puts(profile_msg(p->slowest)); 5646 msg_puts(" "); 5647 msg_advance(38); 5648 msg_puts(profile_msg(p->average)); 5649 msg_puts(" "); 5650 msg_advance(50); 5651 msg_outtrans(highlight_group_name(p->id - 1), 0, false); 5652 msg_puts(" "); 5653 5654 msg_advance(69); 5655 int len; 5656 if (Columns < 80) { 5657 len = 20; // will wrap anyway 5658 } else { 5659 len = Columns - 70; 5660 } 5661 int patlen = (int)strlen(p->pattern); 5662 len = MIN(len, patlen); 5663 msg_outtrans_len(p->pattern, len, 0, false); 5664 msg_puts("\n"); 5665 } 5666 ga_clear(&ga); 5667 if (!got_int) { 5668 msg_puts("\n"); 5669 msg_puts(profile_msg(total_total)); 5670 msg_advance(13); 5671 msg_outnum(total_count); 5672 msg_puts("\n"); 5673 } 5674 }