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