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