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 /* 17 * Structure that stores information about a highlight group. 18 * The ID of a highlight group is also called group ID. It is the index in 19 * the highlight_ga array PLUS ONE. 20 */ 21 struct hl_group 22 { 23 char_u *sg_name; /* highlight group name */ 24 char_u *sg_name_u; /* uppercase of sg_name */ 25 int sg_cleared; /* "hi clear" was used */ 26 /* for normal terminals */ 27 int sg_term; /* "term=" highlighting attributes */ 28 char_u *sg_start; /* terminal string for start highl */ 29 char_u *sg_stop; /* terminal string for stop highl */ 30 int sg_term_attr; /* Screen attr for term mode */ 31 /* for color terminals */ 32 int sg_cterm; /* "cterm=" highlighting attr */ 33 int sg_cterm_bold; /* bold attr was set for light color */ 34 int sg_cterm_fg; /* terminal fg color number + 1 */ 35 int sg_cterm_bg; /* terminal bg color number + 1 */ 36 int sg_cterm_attr; /* Screen attr for color term mode */ 37 /* for when using the GUI */ 38 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 39 guicolor_T sg_gui_fg; /* GUI foreground color handle */ 40 guicolor_T sg_gui_bg; /* GUI background color handle */ 41 #endif 42 #ifdef FEAT_GUI 43 guicolor_T sg_gui_sp; /* GUI special color handle */ 44 GuiFont sg_font; /* GUI font handle */ 45 #ifdef FEAT_XFONTSET 46 GuiFontset sg_fontset; /* GUI fontset handle */ 47 #endif 48 char_u *sg_font_name; /* GUI font or fontset name */ 49 int sg_gui_attr; /* Screen attr for GUI mode */ 50 #endif 51 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 52 /* Store the sp color name for the GUI or synIDattr() */ 53 int sg_gui; /* "gui=" highlighting attributes */ 54 char_u *sg_gui_fg_name;/* GUI foreground color name */ 55 char_u *sg_gui_bg_name;/* GUI background color name */ 56 char_u *sg_gui_sp_name;/* GUI special color name */ 57 #endif 58 int sg_link; /* link to this highlight group ID */ 59 int sg_set; /* combination of SG_* flags */ 60 #ifdef FEAT_EVAL 61 sctx_T sg_script_ctx; /* script in which the group was last set */ 62 #endif 63 }; 64 65 #define SG_TERM 1 /* term has been set */ 66 #define SG_CTERM 2 /* cterm has been set */ 67 #define SG_GUI 4 /* gui has been set */ 68 #define SG_LINK 8 /* link has been set */ 69 70 static garray_T highlight_ga; /* highlight groups for 'highlight' option */ 71 72 #define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data))) 73 74 #define MAX_HL_ID 20000 /* maximum value for a highlight ID. */ 75 76 #ifdef FEAT_CMDL_COMPL 77 /* Flags to indicate an additional string for highlight name completion. */ 78 static int include_none = 0; /* when 1 include "None" */ 79 static int include_default = 0; /* when 1 include "default" */ 80 static int include_link = 0; /* when 2 include "link" and "clear" */ 81 #endif 82 83 /* 84 * The "term", "cterm" and "gui" arguments can be any combination of the 85 * following names, separated by commas (but no spaces!). 86 */ 87 static char *(hl_name_table[]) = 88 {"bold", "standout", "underline", "undercurl", 89 "italic", "reverse", "inverse", "nocombine", "strikethrough", "NONE"}; 90 static int hl_attr_table[] = 91 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, HL_STRIKETHROUGH, 0}; 92 #define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? attr_b : (attr_a)) | (attr_b)) 93 94 static void syn_unadd_group(void); 95 static void set_hl_attr(int idx); 96 static void highlight_list_one(int id); 97 static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name); 98 static int syn_add_group(char_u *name); 99 static int syn_list_header(int did_header, int outlen, int id); 100 static int hl_has_settings(int idx, int check_link); 101 static void highlight_clear(int idx); 102 103 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 104 static void gui_do_one_color(int idx, int do_menu, int do_tooltip); 105 #endif 106 #ifdef FEAT_GUI 107 static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip); 108 static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font); 109 #endif 110 111 /* 112 * An attribute number is the index in attr_table plus ATTR_OFF. 113 */ 114 #define ATTR_OFF (HL_ALL + 1) 115 116 #if defined(FEAT_SYN_HL) || defined(PROTO) 117 118 #define SYN_NAMELEN 50 /* maximum length of a syntax name */ 119 120 /* different types of offsets that are possible */ 121 #define SPO_MS_OFF 0 /* match start offset */ 122 #define SPO_ME_OFF 1 /* match end offset */ 123 #define SPO_HS_OFF 2 /* highl. start offset */ 124 #define SPO_HE_OFF 3 /* highl. end offset */ 125 #define SPO_RS_OFF 4 /* region start offset */ 126 #define SPO_RE_OFF 5 /* region end offset */ 127 #define SPO_LC_OFF 6 /* leading context offset */ 128 #define SPO_COUNT 7 129 130 static char *(spo_name_tab[SPO_COUNT]) = 131 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="}; 132 133 /* 134 * The patterns that are being searched for are stored in a syn_pattern. 135 * A match item consists of one pattern. 136 * A start/end item consists of n start patterns and m end patterns. 137 * A start/skip/end item consists of n start patterns, one skip pattern and m 138 * end patterns. 139 * For the latter two, the patterns are always consecutive: start-skip-end. 140 * 141 * A character offset can be given for the matched text (_m_start and _m_end) 142 * and for the actually highlighted text (_h_start and _h_end). 143 * 144 * Note that ordering of members is optimized to reduce padding. 145 */ 146 typedef struct syn_pattern 147 { 148 char sp_type; /* see SPTYPE_ defines below */ 149 char sp_syncing; /* this item used for syncing */ 150 short sp_syn_match_id; /* highlight group ID of pattern */ 151 short sp_off_flags; /* see below */ 152 int sp_offsets[SPO_COUNT]; /* offsets */ 153 int sp_flags; /* see HL_ defines below */ 154 #ifdef FEAT_CONCEAL 155 int sp_cchar; /* conceal substitute character */ 156 #endif 157 int sp_ic; /* ignore-case flag for sp_prog */ 158 int sp_sync_idx; /* sync item index (syncing only) */ 159 int sp_line_id; /* ID of last line where tried */ 160 int sp_startcol; /* next match in sp_line_id line */ 161 short *sp_cont_list; /* cont. group IDs, if non-zero */ 162 short *sp_next_list; /* next group IDs, if non-zero */ 163 struct sp_syn sp_syn; /* struct passed to in_id_list() */ 164 char_u *sp_pattern; /* regexp to match, pattern */ 165 regprog_T *sp_prog; /* regexp to match, program */ 166 #ifdef FEAT_PROFILE 167 syn_time_T sp_time; 168 #endif 169 } synpat_T; 170 171 /* The sp_off_flags are computed like this: 172 * offset from the start of the matched text: (1 << SPO_XX_OFF) 173 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT)) 174 * When both are present, only one is used. 175 */ 176 177 #define SPTYPE_MATCH 1 /* match keyword with this group ID */ 178 #define SPTYPE_START 2 /* match a regexp, start of item */ 179 #define SPTYPE_END 3 /* match a regexp, end of item */ 180 #define SPTYPE_SKIP 4 /* match a regexp, skip within item */ 181 182 183 #define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data)) 184 185 #define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */ 186 187 /* 188 * Flags for b_syn_sync_flags: 189 */ 190 #define SF_CCOMMENT 0x01 /* sync on a C-style comment */ 191 #define SF_MATCH 0x02 /* sync by matching a pattern */ 192 193 #define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data)) 194 195 #define MAXKEYWLEN 80 /* maximum length of a keyword */ 196 197 /* 198 * The attributes of the syntax item that has been recognized. 199 */ 200 static int current_attr = 0; /* attr of current syntax word */ 201 #ifdef FEAT_EVAL 202 static int current_id = 0; /* ID of current char for syn_get_id() */ 203 static int current_trans_id = 0; /* idem, transparency removed */ 204 #endif 205 #ifdef FEAT_CONCEAL 206 static int current_flags = 0; 207 static int current_seqnr = 0; 208 static int current_sub_char = 0; 209 #endif 210 211 typedef struct syn_cluster_S 212 { 213 char_u *scl_name; /* syntax cluster name */ 214 char_u *scl_name_u; /* uppercase of scl_name */ 215 short *scl_list; /* IDs in this syntax cluster */ 216 } syn_cluster_T; 217 218 /* 219 * Methods of combining two clusters 220 */ 221 #define CLUSTER_REPLACE 1 /* replace first list with second */ 222 #define CLUSTER_ADD 2 /* add second list to first */ 223 #define CLUSTER_SUBTRACT 3 /* subtract second list from first */ 224 225 #define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data)) 226 227 /* 228 * Syntax group IDs have different types: 229 * 0 - 19999 normal syntax groups 230 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added) 231 * 21000 - 21999 TOP indicator (current_syn_inc_tag added) 232 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added) 233 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID) 234 */ 235 #define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */ 236 #define SYNID_TOP 21000 /* syntax group ID for contains=TOP */ 237 #define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */ 238 #define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */ 239 240 #define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */ 241 #define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER) 242 243 /* 244 * Annoying Hack(TM): ":syn include" needs this pointer to pass to 245 * expand_filename(). Most of the other syntax commands don't need it, so 246 * instead of passing it to them, we stow it here. 247 */ 248 static char_u **syn_cmdlinep; 249 250 /* 251 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d 252 * files from leaking into ALLBUT lists, we assign a unique ID to the 253 * rules in each ":syn include"'d file. 254 */ 255 static int current_syn_inc_tag = 0; 256 static int running_syn_inc_tag = 0; 257 258 /* 259 * In a hashtable item "hi_key" points to "keyword" in a keyentry. 260 * This avoids adding a pointer to the hashtable item. 261 * KE2HIKEY() converts a var pointer to a hashitem key pointer. 262 * HIKEY2KE() converts a hashitem key pointer to a var pointer. 263 * HI2KE() converts a hashitem pointer to a var pointer. 264 */ 265 static keyentry_T dumkey; 266 #define KE2HIKEY(kp) ((kp)->keyword) 267 #define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey))) 268 #define HI2KE(hi) HIKEY2KE((hi)->hi_key) 269 270 /* 271 * To reduce the time spent in keepend(), remember at which level in the state 272 * stack the first item with "keepend" is present. When "-1", there is no 273 * "keepend" on the stack. 274 */ 275 static int keepend_level = -1; 276 277 static char msg_no_items[] = N_("No Syntax items defined for this buffer"); 278 279 /* 280 * For the current state we need to remember more than just the idx. 281 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown. 282 * (The end positions have the column number of the next char) 283 */ 284 typedef struct state_item 285 { 286 int si_idx; /* index of syntax pattern or 287 KEYWORD_IDX */ 288 int si_id; /* highlight group ID for keywords */ 289 int si_trans_id; /* idem, transparency removed */ 290 int si_m_lnum; /* lnum of the match */ 291 int si_m_startcol; /* starting column of the match */ 292 lpos_T si_m_endpos; /* just after end posn of the match */ 293 lpos_T si_h_startpos; /* start position of the highlighting */ 294 lpos_T si_h_endpos; /* end position of the highlighting */ 295 lpos_T si_eoe_pos; /* end position of end pattern */ 296 int si_end_idx; /* group ID for end pattern or zero */ 297 int si_ends; /* if match ends before si_m_endpos */ 298 int si_attr; /* attributes in this state */ 299 long si_flags; /* HL_HAS_EOL flag in this state, and 300 * HL_SKIP* for si_next_list */ 301 #ifdef FEAT_CONCEAL 302 int si_seqnr; /* sequence number */ 303 int si_cchar; /* substitution character for conceal */ 304 #endif 305 short *si_cont_list; /* list of contained groups */ 306 short *si_next_list; /* nextgroup IDs after this item ends */ 307 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start 308 * pattern */ 309 } stateitem_T; 310 311 #define KEYWORD_IDX -1 /* value of si_idx for keywords */ 312 #define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all 313 but contained groups */ 314 315 #ifdef FEAT_CONCEAL 316 static int next_seqnr = 1; /* value to use for si_seqnr */ 317 #endif 318 319 /* 320 * Struct to reduce the number of arguments to get_syn_options(), it's used 321 * very often. 322 */ 323 typedef struct 324 { 325 int flags; /* flags for contained and transparent */ 326 int keyword; /* TRUE for ":syn keyword" */ 327 int *sync_idx; /* syntax item for "grouphere" argument, NULL 328 if not allowed */ 329 char has_cont_list; /* TRUE if "cont_list" can be used */ 330 short *cont_list; /* group IDs for "contains" argument */ 331 short *cont_in_list; /* group IDs for "containedin" argument */ 332 short *next_list; /* group IDs for "nextgroup" argument */ 333 } syn_opt_arg_T; 334 335 /* 336 * The next possible match in the current line for any pattern is remembered, 337 * to avoid having to try for a match in each column. 338 * If next_match_idx == -1, not tried (in this line) yet. 339 * If next_match_col == MAXCOL, no match found in this line. 340 * (All end positions have the column of the char after the end) 341 */ 342 static int next_match_col; /* column for start of next match */ 343 static lpos_T next_match_m_endpos; /* position for end of next match */ 344 static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */ 345 static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */ 346 static int next_match_idx; /* index of matched item */ 347 static long next_match_flags; /* flags for next match */ 348 static lpos_T next_match_eos_pos; /* end of start pattn (start region) */ 349 static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */ 350 static int next_match_end_idx; /* ID of group for end pattn or zero */ 351 static reg_extmatch_T *next_match_extmatch = NULL; 352 353 /* 354 * A state stack is an array of integers or stateitem_T, stored in a 355 * garray_T. A state stack is invalid if its itemsize entry is zero. 356 */ 357 #define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0) 358 #define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0) 359 360 /* 361 * The current state (within the line) of the recognition engine. 362 * When current_state.ga_itemsize is 0 the current state is invalid. 363 */ 364 static win_T *syn_win; /* current window for highlighting */ 365 static buf_T *syn_buf; /* current buffer for highlighting */ 366 static synblock_T *syn_block; /* current buffer for highlighting */ 367 #ifdef FEAT_RELTIME 368 static proftime_T *syn_tm; /* timeout limit */ 369 #endif 370 static linenr_T current_lnum = 0; /* lnum of current state */ 371 static colnr_T current_col = 0; /* column of current state */ 372 static int current_state_stored = 0; /* TRUE if stored current state 373 * after setting current_finished */ 374 static int current_finished = 0; /* current line has been finished */ 375 static garray_T current_state /* current stack of state_items */ 376 = {0, 0, 0, 0, NULL}; 377 static short *current_next_list = NULL; /* when non-zero, nextgroup list */ 378 static int current_next_flags = 0; /* flags for current_next_list */ 379 static int current_line_id = 0; /* unique number for current line */ 380 381 #define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx] 382 383 static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid); 384 static int syn_match_linecont(linenr_T lnum); 385 static void syn_start_line(void); 386 static void syn_update_ends(int startofline); 387 static void syn_stack_alloc(void); 388 static int syn_stack_cleanup(void); 389 static void syn_stack_free_entry(synblock_T *block, synstate_T *p); 390 static synstate_T *syn_stack_find_entry(linenr_T lnum); 391 static synstate_T *store_current_state(void); 392 static void load_current_state(synstate_T *from); 393 static void invalidate_current_state(void); 394 static int syn_stack_equal(synstate_T *sp); 395 static void validate_current_state(void); 396 static int syn_finish_line(int syncing); 397 static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state); 398 static int did_match_already(int idx, garray_T *gap); 399 static stateitem_T *push_next_match(stateitem_T *cur_si); 400 static void check_state_ends(void); 401 static void update_si_attr(int idx); 402 static void check_keepend(void); 403 static void update_si_end(stateitem_T *sip, int startcol, int force); 404 static short *copy_id_list(short *list); 405 static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained); 406 static int push_current_state(int idx); 407 static void pop_current_state(void); 408 #ifdef FEAT_PROFILE 409 static void syn_clear_time(syn_time_T *tt); 410 static void syntime_clear(void); 411 static void syntime_report(void); 412 static int syn_time_on = FALSE; 413 # define IF_SYN_TIME(p) (p) 414 #else 415 # define IF_SYN_TIME(p) NULL 416 typedef int syn_time_T; 417 #endif 418 419 static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf); 420 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); 421 422 static void limit_pos(lpos_T *pos, lpos_T *limit); 423 static void limit_pos_zero(lpos_T *pos, lpos_T *limit); 424 static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra); 425 static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra); 426 static char_u *syn_getcurline(void); 427 static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st); 428 static int check_keyword_id(char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp); 429 static void syn_remove_pattern(synblock_T *block, int idx); 430 static void syn_clear_pattern(synblock_T *block, int i); 431 static void syn_clear_cluster(synblock_T *block, int i); 432 static void syn_clear_one(int id, int syncing); 433 static void syn_cmd_onoff(exarg_T *eap, char *name); 434 static void syn_lines_msg(void); 435 static void syn_match_msg(void); 436 static void syn_list_one(int id, int syncing, int link_only); 437 static void syn_list_cluster(int id); 438 static void put_id_list(char_u *name, short *list, int attr); 439 static void put_pattern(char *s, int c, synpat_T *spp, int attr); 440 static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr); 441 static void syn_clear_keyword(int id, hashtab_T *ht); 442 static void clear_keywtab(hashtab_T *ht); 443 static int syn_scl_namen2id(char_u *linep, int len); 444 static int syn_check_cluster(char_u *pp, int len); 445 static int syn_add_cluster(char_u *name); 446 static void init_syn_patterns(void); 447 static char_u *get_syn_pattern(char_u *arg, synpat_T *ci); 448 static int get_id_list(char_u **arg, int keylen, short **list, int skip); 449 static void syn_combine_list(short **clstr1, short **clstr2, int list_op); 450 451 #if defined(FEAT_RELTIME) || defined(PROTO) 452 /* 453 * Set the timeout used for syntax highlighting. 454 * Use NULL to reset, no timeout. 455 */ 456 void 457 syn_set_timeout(proftime_T *tm) 458 { 459 syn_tm = tm; 460 } 461 #endif 462 463 /* 464 * Start the syntax recognition for a line. This function is normally called 465 * from the screen updating, once for each displayed line. 466 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get 467 * it. Careful: curbuf and curwin are likely to point to another buffer and 468 * window. 469 */ 470 void 471 syntax_start(win_T *wp, linenr_T lnum) 472 { 473 synstate_T *p; 474 synstate_T *last_valid = NULL; 475 synstate_T *last_min_valid = NULL; 476 synstate_T *sp, *prev = NULL; 477 linenr_T parsed_lnum; 478 linenr_T first_stored; 479 int dist; 480 static varnumber_T changedtick = 0; /* remember the last change ID */ 481 482 #ifdef FEAT_CONCEAL 483 current_sub_char = NUL; 484 #endif 485 486 /* 487 * After switching buffers, invalidate current_state. 488 * Also do this when a change was made, the current state may be invalid 489 * then. 490 */ 491 if (syn_block != wp->w_s 492 || syn_buf != wp->w_buffer 493 || changedtick != CHANGEDTICK(syn_buf)) 494 { 495 invalidate_current_state(); 496 syn_buf = wp->w_buffer; 497 syn_block = wp->w_s; 498 } 499 changedtick = CHANGEDTICK(syn_buf); 500 syn_win = wp; 501 502 /* 503 * Allocate syntax stack when needed. 504 */ 505 syn_stack_alloc(); 506 if (syn_block->b_sst_array == NULL) 507 return; /* out of memory */ 508 syn_block->b_sst_lasttick = display_tick; 509 510 /* 511 * If the state of the end of the previous line is useful, store it. 512 */ 513 if (VALID_STATE(¤t_state) 514 && current_lnum < lnum 515 && current_lnum < syn_buf->b_ml.ml_line_count) 516 { 517 (void)syn_finish_line(FALSE); 518 if (!current_state_stored) 519 { 520 ++current_lnum; 521 (void)store_current_state(); 522 } 523 524 /* 525 * If the current_lnum is now the same as "lnum", keep the current 526 * state (this happens very often!). Otherwise invalidate 527 * current_state and figure it out below. 528 */ 529 if (current_lnum != lnum) 530 invalidate_current_state(); 531 } 532 else 533 invalidate_current_state(); 534 535 /* 536 * Try to synchronize from a saved state in b_sst_array[]. 537 * Only do this if lnum is not before and not to far beyond a saved state. 538 */ 539 if (INVALID_STATE(¤t_state) && syn_block->b_sst_array != NULL) 540 { 541 /* Find last valid saved state before start_lnum. */ 542 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next) 543 { 544 if (p->sst_lnum > lnum) 545 break; 546 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0) 547 { 548 last_valid = p; 549 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines) 550 last_min_valid = p; 551 } 552 } 553 if (last_min_valid != NULL) 554 load_current_state(last_min_valid); 555 } 556 557 /* 558 * If "lnum" is before or far beyond a line with a saved state, need to 559 * re-synchronize. 560 */ 561 if (INVALID_STATE(¤t_state)) 562 { 563 syn_sync(wp, lnum, last_valid); 564 if (current_lnum == 1) 565 /* First line is always valid, no matter "minlines". */ 566 first_stored = 1; 567 else 568 /* Need to parse "minlines" lines before state can be considered 569 * valid to store. */ 570 first_stored = current_lnum + syn_block->b_syn_sync_minlines; 571 } 572 else 573 first_stored = current_lnum; 574 575 /* 576 * Advance from the sync point or saved state until the current line. 577 * Save some entries for syncing with later on. 578 */ 579 if (syn_block->b_sst_len <= Rows) 580 dist = 999999; 581 else 582 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1; 583 while (current_lnum < lnum) 584 { 585 syn_start_line(); 586 (void)syn_finish_line(FALSE); 587 ++current_lnum; 588 589 /* If we parsed at least "minlines" lines or started at a valid 590 * state, the current state is considered valid. */ 591 if (current_lnum >= first_stored) 592 { 593 /* Check if the saved state entry is for the current line and is 594 * equal to the current state. If so, then validate all saved 595 * states that depended on a change before the parsed line. */ 596 if (prev == NULL) 597 prev = syn_stack_find_entry(current_lnum - 1); 598 if (prev == NULL) 599 sp = syn_block->b_sst_first; 600 else 601 sp = prev; 602 while (sp != NULL && sp->sst_lnum < current_lnum) 603 sp = sp->sst_next; 604 if (sp != NULL 605 && sp->sst_lnum == current_lnum 606 && syn_stack_equal(sp)) 607 { 608 parsed_lnum = current_lnum; 609 prev = sp; 610 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum) 611 { 612 if (sp->sst_lnum <= lnum) 613 /* valid state before desired line, use this one */ 614 prev = sp; 615 else if (sp->sst_change_lnum == 0) 616 /* past saved states depending on change, break here. */ 617 break; 618 sp->sst_change_lnum = 0; 619 sp = sp->sst_next; 620 } 621 load_current_state(prev); 622 } 623 /* Store the state at this line when it's the first one, the line 624 * where we start parsing, or some distance from the previously 625 * saved state. But only when parsed at least 'minlines'. */ 626 else if (prev == NULL 627 || current_lnum == lnum 628 || current_lnum >= prev->sst_lnum + dist) 629 prev = store_current_state(); 630 } 631 632 /* This can take a long time: break when CTRL-C pressed. The current 633 * state will be wrong then. */ 634 line_breakcheck(); 635 if (got_int) 636 { 637 current_lnum = lnum; 638 break; 639 } 640 } 641 642 syn_start_line(); 643 } 644 645 /* 646 * We cannot simply discard growarrays full of state_items or buf_states; we 647 * have to manually release their extmatch pointers first. 648 */ 649 static void 650 clear_syn_state(synstate_T *p) 651 { 652 int i; 653 garray_T *gap; 654 655 if (p->sst_stacksize > SST_FIX_STATES) 656 { 657 gap = &(p->sst_union.sst_ga); 658 for (i = 0; i < gap->ga_len; i++) 659 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch); 660 ga_clear(gap); 661 } 662 else 663 { 664 for (i = 0; i < p->sst_stacksize; i++) 665 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch); 666 } 667 } 668 669 /* 670 * Cleanup the current_state stack. 671 */ 672 static void 673 clear_current_state(void) 674 { 675 int i; 676 stateitem_T *sip; 677 678 sip = (stateitem_T *)(current_state.ga_data); 679 for (i = 0; i < current_state.ga_len; i++) 680 unref_extmatch(sip[i].si_extmatch); 681 ga_clear(¤t_state); 682 } 683 684 /* 685 * Try to find a synchronisation point for line "lnum". 686 * 687 * This sets current_lnum and the current state. One of three methods is 688 * used: 689 * 1. Search backwards for the end of a C-comment. 690 * 2. Search backwards for given sync patterns. 691 * 3. Simply start on a given number of lines above "lnum". 692 */ 693 static void 694 syn_sync( 695 win_T *wp, 696 linenr_T start_lnum, 697 synstate_T *last_valid) 698 { 699 buf_T *curbuf_save; 700 win_T *curwin_save; 701 pos_T cursor_save; 702 int idx; 703 linenr_T lnum; 704 linenr_T end_lnum; 705 linenr_T break_lnum; 706 int had_sync_point; 707 stateitem_T *cur_si; 708 synpat_T *spp; 709 char_u *line; 710 int found_flags = 0; 711 int found_match_idx = 0; 712 linenr_T found_current_lnum = 0; 713 int found_current_col= 0; 714 lpos_T found_m_endpos; 715 colnr_T prev_current_col; 716 717 /* 718 * Clear any current state that might be hanging around. 719 */ 720 invalidate_current_state(); 721 722 /* 723 * Start at least "minlines" back. Default starting point for parsing is 724 * there. 725 * Start further back, to avoid that scrolling backwards will result in 726 * resyncing for every line. Now it resyncs only one out of N lines, 727 * where N is minlines * 1.5, or minlines * 2 if minlines is small. 728 * Watch out for overflow when minlines is MAXLNUM. 729 */ 730 if (syn_block->b_syn_sync_minlines > start_lnum) 731 start_lnum = 1; 732 else 733 { 734 if (syn_block->b_syn_sync_minlines == 1) 735 lnum = 1; 736 else if (syn_block->b_syn_sync_minlines < 10) 737 lnum = syn_block->b_syn_sync_minlines * 2; 738 else 739 lnum = syn_block->b_syn_sync_minlines * 3 / 2; 740 if (syn_block->b_syn_sync_maxlines != 0 741 && lnum > syn_block->b_syn_sync_maxlines) 742 lnum = syn_block->b_syn_sync_maxlines; 743 if (lnum >= start_lnum) 744 start_lnum = 1; 745 else 746 start_lnum -= lnum; 747 } 748 current_lnum = start_lnum; 749 750 /* 751 * 1. Search backwards for the end of a C-style comment. 752 */ 753 if (syn_block->b_syn_sync_flags & SF_CCOMMENT) 754 { 755 /* Need to make syn_buf the current buffer for a moment, to be able to 756 * use find_start_comment(). */ 757 curwin_save = curwin; 758 curwin = wp; 759 curbuf_save = curbuf; 760 curbuf = syn_buf; 761 762 /* 763 * Skip lines that end in a backslash. 764 */ 765 for ( ; start_lnum > 1; --start_lnum) 766 { 767 line = ml_get(start_lnum - 1); 768 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\') 769 break; 770 } 771 current_lnum = start_lnum; 772 773 /* set cursor to start of search */ 774 cursor_save = wp->w_cursor; 775 wp->w_cursor.lnum = start_lnum; 776 wp->w_cursor.col = 0; 777 778 /* 779 * If the line is inside a comment, need to find the syntax item that 780 * defines the comment. 781 * Restrict the search for the end of a comment to b_syn_sync_maxlines. 782 */ 783 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL) 784 { 785 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; ) 786 if (SYN_ITEMS(syn_block)[idx].sp_syn.id 787 == syn_block->b_syn_sync_id 788 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START) 789 { 790 validate_current_state(); 791 if (push_current_state(idx) == OK) 792 update_si_attr(current_state.ga_len - 1); 793 break; 794 } 795 } 796 797 /* restore cursor and buffer */ 798 wp->w_cursor = cursor_save; 799 curwin = curwin_save; 800 curbuf = curbuf_save; 801 } 802 803 /* 804 * 2. Search backwards for given sync patterns. 805 */ 806 else if (syn_block->b_syn_sync_flags & SF_MATCH) 807 { 808 if (syn_block->b_syn_sync_maxlines != 0 809 && start_lnum > syn_block->b_syn_sync_maxlines) 810 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines; 811 else 812 break_lnum = 0; 813 814 found_m_endpos.lnum = 0; 815 found_m_endpos.col = 0; 816 end_lnum = start_lnum; 817 lnum = start_lnum; 818 while (--lnum > break_lnum) 819 { 820 /* This can take a long time: break when CTRL-C pressed. */ 821 line_breakcheck(); 822 if (got_int) 823 { 824 invalidate_current_state(); 825 current_lnum = start_lnum; 826 break; 827 } 828 829 /* Check if we have run into a valid saved state stack now. */ 830 if (last_valid != NULL && lnum == last_valid->sst_lnum) 831 { 832 load_current_state(last_valid); 833 break; 834 } 835 836 /* 837 * Check if the previous line has the line-continuation pattern. 838 */ 839 if (lnum > 1 && syn_match_linecont(lnum - 1)) 840 continue; 841 842 /* 843 * Start with nothing on the state stack 844 */ 845 validate_current_state(); 846 847 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum) 848 { 849 syn_start_line(); 850 for (;;) 851 { 852 had_sync_point = syn_finish_line(TRUE); 853 /* 854 * When a sync point has been found, remember where, and 855 * continue to look for another one, further on in the line. 856 */ 857 if (had_sync_point && current_state.ga_len) 858 { 859 cur_si = &CUR_STATE(current_state.ga_len - 1); 860 if (cur_si->si_m_endpos.lnum > start_lnum) 861 { 862 /* ignore match that goes to after where started */ 863 current_lnum = end_lnum; 864 break; 865 } 866 if (cur_si->si_idx < 0) 867 { 868 /* Cannot happen? */ 869 found_flags = 0; 870 found_match_idx = KEYWORD_IDX; 871 } 872 else 873 { 874 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]); 875 found_flags = spp->sp_flags; 876 found_match_idx = spp->sp_sync_idx; 877 } 878 found_current_lnum = current_lnum; 879 found_current_col = current_col; 880 found_m_endpos = cur_si->si_m_endpos; 881 /* 882 * Continue after the match (be aware of a zero-length 883 * match). 884 */ 885 if (found_m_endpos.lnum > current_lnum) 886 { 887 current_lnum = found_m_endpos.lnum; 888 current_col = found_m_endpos.col; 889 if (current_lnum >= end_lnum) 890 break; 891 } 892 else if (found_m_endpos.col > current_col) 893 current_col = found_m_endpos.col; 894 else 895 ++current_col; 896 897 /* syn_current_attr() will have skipped the check for 898 * an item that ends here, need to do that now. Be 899 * careful not to go past the NUL. */ 900 prev_current_col = current_col; 901 if (syn_getcurline()[current_col] != NUL) 902 ++current_col; 903 check_state_ends(); 904 current_col = prev_current_col; 905 } 906 else 907 break; 908 } 909 } 910 911 /* 912 * If a sync point was encountered, break here. 913 */ 914 if (found_flags) 915 { 916 /* 917 * Put the item that was specified by the sync point on the 918 * state stack. If there was no item specified, make the 919 * state stack empty. 920 */ 921 clear_current_state(); 922 if (found_match_idx >= 0 923 && push_current_state(found_match_idx) == OK) 924 update_si_attr(current_state.ga_len - 1); 925 926 /* 927 * When using "grouphere", continue from the sync point 928 * match, until the end of the line. Parsing starts at 929 * the next line. 930 * For "groupthere" the parsing starts at start_lnum. 931 */ 932 if (found_flags & HL_SYNC_HERE) 933 { 934 if (current_state.ga_len) 935 { 936 cur_si = &CUR_STATE(current_state.ga_len - 1); 937 cur_si->si_h_startpos.lnum = found_current_lnum; 938 cur_si->si_h_startpos.col = found_current_col; 939 update_si_end(cur_si, (int)current_col, TRUE); 940 check_keepend(); 941 } 942 current_col = found_m_endpos.col; 943 current_lnum = found_m_endpos.lnum; 944 (void)syn_finish_line(FALSE); 945 ++current_lnum; 946 } 947 else 948 current_lnum = start_lnum; 949 950 break; 951 } 952 953 end_lnum = lnum; 954 invalidate_current_state(); 955 } 956 957 /* Ran into start of the file or exceeded maximum number of lines */ 958 if (lnum <= break_lnum) 959 { 960 invalidate_current_state(); 961 current_lnum = break_lnum + 1; 962 } 963 } 964 965 validate_current_state(); 966 } 967 968 static void 969 save_chartab(char_u *chartab) 970 { 971 if (syn_block->b_syn_isk != empty_option) 972 { 973 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32); 974 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab, 975 (size_t)32); 976 } 977 } 978 979 static void 980 restore_chartab(char_u *chartab) 981 { 982 if (syn_win->w_s->b_syn_isk != empty_option) 983 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32); 984 } 985 986 /* 987 * Return TRUE if the line-continuation pattern matches in line "lnum". 988 */ 989 static int 990 syn_match_linecont(linenr_T lnum) 991 { 992 regmmatch_T regmatch; 993 int r; 994 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */ 995 996 if (syn_block->b_syn_linecont_prog != NULL) 997 { 998 /* use syntax iskeyword option */ 999 save_chartab(buf_chartab); 1000 regmatch.rmm_ic = syn_block->b_syn_linecont_ic; 1001 regmatch.regprog = syn_block->b_syn_linecont_prog; 1002 r = syn_regexec(®match, lnum, (colnr_T)0, 1003 IF_SYN_TIME(&syn_block->b_syn_linecont_time)); 1004 syn_block->b_syn_linecont_prog = regmatch.regprog; 1005 restore_chartab(buf_chartab); 1006 return r; 1007 } 1008 return FALSE; 1009 } 1010 1011 /* 1012 * Prepare the current state for the start of a line. 1013 */ 1014 static void 1015 syn_start_line(void) 1016 { 1017 current_finished = FALSE; 1018 current_col = 0; 1019 1020 /* 1021 * Need to update the end of a start/skip/end that continues from the 1022 * previous line and regions that have "keepend". 1023 */ 1024 if (current_state.ga_len > 0) 1025 { 1026 syn_update_ends(TRUE); 1027 check_state_ends(); 1028 } 1029 1030 next_match_idx = -1; 1031 ++current_line_id; 1032 #ifdef FEAT_CONCEAL 1033 next_seqnr = 1; 1034 #endif 1035 } 1036 1037 /* 1038 * Check for items in the stack that need their end updated. 1039 * When "startofline" is TRUE the last item is always updated. 1040 * When "startofline" is FALSE the item with "keepend" is forcefully updated. 1041 */ 1042 static void 1043 syn_update_ends(int startofline) 1044 { 1045 stateitem_T *cur_si; 1046 int i; 1047 int seen_keepend; 1048 1049 if (startofline) 1050 { 1051 /* Check for a match carried over from a previous line with a 1052 * contained region. The match ends as soon as the region ends. */ 1053 for (i = 0; i < current_state.ga_len; ++i) 1054 { 1055 cur_si = &CUR_STATE(i); 1056 if (cur_si->si_idx >= 0 1057 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type 1058 == SPTYPE_MATCH 1059 && cur_si->si_m_endpos.lnum < current_lnum) 1060 { 1061 cur_si->si_flags |= HL_MATCHCONT; 1062 cur_si->si_m_endpos.lnum = 0; 1063 cur_si->si_m_endpos.col = 0; 1064 cur_si->si_h_endpos = cur_si->si_m_endpos; 1065 cur_si->si_ends = TRUE; 1066 } 1067 } 1068 } 1069 1070 /* 1071 * Need to update the end of a start/skip/end that continues from the 1072 * previous line. And regions that have "keepend", because they may 1073 * influence contained items. If we've just removed "extend" 1074 * (startofline == 0) then we should update ends of normal regions 1075 * contained inside "keepend" because "extend" could have extended 1076 * these "keepend" regions as well as contained normal regions. 1077 * Then check for items ending in column 0. 1078 */ 1079 i = current_state.ga_len - 1; 1080 if (keepend_level >= 0) 1081 for ( ; i > keepend_level; --i) 1082 if (CUR_STATE(i).si_flags & HL_EXTEND) 1083 break; 1084 1085 seen_keepend = FALSE; 1086 for ( ; i < current_state.ga_len; ++i) 1087 { 1088 cur_si = &CUR_STATE(i); 1089 if ((cur_si->si_flags & HL_KEEPEND) 1090 || (seen_keepend && !startofline) 1091 || (i == current_state.ga_len - 1 && startofline)) 1092 { 1093 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */ 1094 cur_si->si_h_startpos.lnum = current_lnum; 1095 1096 if (!(cur_si->si_flags & HL_MATCHCONT)) 1097 update_si_end(cur_si, (int)current_col, !startofline); 1098 1099 if (!startofline && (cur_si->si_flags & HL_KEEPEND)) 1100 seen_keepend = TRUE; 1101 } 1102 } 1103 check_keepend(); 1104 } 1105 1106 /**************************************** 1107 * Handling of the state stack cache. 1108 */ 1109 1110 /* 1111 * EXPLANATION OF THE SYNTAX STATE STACK CACHE 1112 * 1113 * To speed up syntax highlighting, the state stack for the start of some 1114 * lines is cached. These entries can be used to start parsing at that point. 1115 * 1116 * The stack is kept in b_sst_array[] for each buffer. There is a list of 1117 * valid entries. b_sst_first points to the first one, then follow sst_next. 1118 * The entries are sorted on line number. The first entry is often for line 2 1119 * (line 1 always starts with an empty stack). 1120 * There is also a list for free entries. This construction is used to avoid 1121 * having to allocate and free memory blocks too often. 1122 * 1123 * When making changes to the buffer, this is logged in b_mod_*. When calling 1124 * update_screen() to update the display, it will call 1125 * syn_stack_apply_changes() for each displayed buffer to adjust the cached 1126 * entries. The entries which are inside the changed area are removed, 1127 * because they must be recomputed. Entries below the changed have their line 1128 * number adjusted for deleted/inserted lines, and have their sst_change_lnum 1129 * set to indicate that a check must be made if the changed lines would change 1130 * the cached entry. 1131 * 1132 * When later displaying lines, an entry is stored for each line. Displayed 1133 * lines are likely to be displayed again, in which case the state at the 1134 * start of the line is needed. 1135 * For not displayed lines, an entry is stored for every so many lines. These 1136 * entries will be used e.g., when scrolling backwards. The distance between 1137 * entries depends on the number of lines in the buffer. For small buffers 1138 * the distance is fixed at SST_DIST, for large buffers there is a fixed 1139 * number of entries SST_MAX_ENTRIES, and the distance is computed. 1140 */ 1141 1142 static void 1143 syn_stack_free_block(synblock_T *block) 1144 { 1145 synstate_T *p; 1146 1147 if (block->b_sst_array != NULL) 1148 { 1149 for (p = block->b_sst_first; p != NULL; p = p->sst_next) 1150 clear_syn_state(p); 1151 VIM_CLEAR(block->b_sst_array); 1152 block->b_sst_first = NULL; 1153 block->b_sst_len = 0; 1154 } 1155 } 1156 /* 1157 * Free b_sst_array[] for buffer "buf". 1158 * Used when syntax items changed to force resyncing everywhere. 1159 */ 1160 void 1161 syn_stack_free_all(synblock_T *block) 1162 { 1163 #ifdef FEAT_FOLDING 1164 win_T *wp; 1165 #endif 1166 1167 syn_stack_free_block(block); 1168 1169 #ifdef FEAT_FOLDING 1170 /* When using "syntax" fold method, must update all folds. */ 1171 FOR_ALL_WINDOWS(wp) 1172 { 1173 if (wp->w_s == block && foldmethodIsSyntax(wp)) 1174 foldUpdateAll(wp); 1175 } 1176 #endif 1177 } 1178 1179 /* 1180 * Allocate the syntax state stack for syn_buf when needed. 1181 * If the number of entries in b_sst_array[] is much too big or a bit too 1182 * small, reallocate it. 1183 * Also used to allocate b_sst_array[] for the first time. 1184 */ 1185 static void 1186 syn_stack_alloc(void) 1187 { 1188 long len; 1189 synstate_T *to, *from; 1190 synstate_T *sstp; 1191 1192 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2; 1193 if (len < SST_MIN_ENTRIES) 1194 len = SST_MIN_ENTRIES; 1195 else if (len > SST_MAX_ENTRIES) 1196 len = SST_MAX_ENTRIES; 1197 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len) 1198 { 1199 /* Allocate 50% too much, to avoid reallocating too often. */ 1200 len = syn_buf->b_ml.ml_line_count; 1201 len = (len + len / 2) / SST_DIST + Rows * 2; 1202 if (len < SST_MIN_ENTRIES) 1203 len = SST_MIN_ENTRIES; 1204 else if (len > SST_MAX_ENTRIES) 1205 len = SST_MAX_ENTRIES; 1206 1207 if (syn_block->b_sst_array != NULL) 1208 { 1209 /* When shrinking the array, cleanup the existing stack. 1210 * Make sure that all valid entries fit in the new array. */ 1211 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len 1212 && syn_stack_cleanup()) 1213 ; 1214 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2) 1215 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2; 1216 } 1217 1218 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T))); 1219 if (sstp == NULL) /* out of memory! */ 1220 return; 1221 1222 to = sstp - 1; 1223 if (syn_block->b_sst_array != NULL) 1224 { 1225 /* Move the states from the old array to the new one. */ 1226 for (from = syn_block->b_sst_first; from != NULL; 1227 from = from->sst_next) 1228 { 1229 ++to; 1230 *to = *from; 1231 to->sst_next = to + 1; 1232 } 1233 } 1234 if (to != sstp - 1) 1235 { 1236 to->sst_next = NULL; 1237 syn_block->b_sst_first = sstp; 1238 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1; 1239 } 1240 else 1241 { 1242 syn_block->b_sst_first = NULL; 1243 syn_block->b_sst_freecount = len; 1244 } 1245 1246 /* Create the list of free entries. */ 1247 syn_block->b_sst_firstfree = to + 1; 1248 while (++to < sstp + len) 1249 to->sst_next = to + 1; 1250 (sstp + len - 1)->sst_next = NULL; 1251 1252 vim_free(syn_block->b_sst_array); 1253 syn_block->b_sst_array = sstp; 1254 syn_block->b_sst_len = len; 1255 } 1256 } 1257 1258 /* 1259 * Check for changes in a buffer to affect stored syntax states. Uses the 1260 * b_mod_* fields. 1261 * Called from update_screen(), before screen is being updated, once for each 1262 * displayed buffer. 1263 */ 1264 void 1265 syn_stack_apply_changes(buf_T *buf) 1266 { 1267 win_T *wp; 1268 1269 syn_stack_apply_changes_block(&buf->b_s, buf); 1270 1271 FOR_ALL_WINDOWS(wp) 1272 { 1273 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s)) 1274 syn_stack_apply_changes_block(wp->w_s, buf); 1275 } 1276 } 1277 1278 static void 1279 syn_stack_apply_changes_block(synblock_T *block, buf_T *buf) 1280 { 1281 synstate_T *p, *prev, *np; 1282 linenr_T n; 1283 1284 prev = NULL; 1285 for (p = block->b_sst_first; p != NULL; ) 1286 { 1287 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top) 1288 { 1289 n = p->sst_lnum + buf->b_mod_xlines; 1290 if (n <= buf->b_mod_bot) 1291 { 1292 /* this state is inside the changed area, remove it */ 1293 np = p->sst_next; 1294 if (prev == NULL) 1295 block->b_sst_first = np; 1296 else 1297 prev->sst_next = np; 1298 syn_stack_free_entry(block, p); 1299 p = np; 1300 continue; 1301 } 1302 /* This state is below the changed area. Remember the line 1303 * that needs to be parsed before this entry can be made valid 1304 * again. */ 1305 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top) 1306 { 1307 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top) 1308 p->sst_change_lnum += buf->b_mod_xlines; 1309 else 1310 p->sst_change_lnum = buf->b_mod_top; 1311 } 1312 if (p->sst_change_lnum == 0 1313 || p->sst_change_lnum < buf->b_mod_bot) 1314 p->sst_change_lnum = buf->b_mod_bot; 1315 1316 p->sst_lnum = n; 1317 } 1318 prev = p; 1319 p = p->sst_next; 1320 } 1321 } 1322 1323 /* 1324 * Reduce the number of entries in the state stack for syn_buf. 1325 * Returns TRUE if at least one entry was freed. 1326 */ 1327 static int 1328 syn_stack_cleanup(void) 1329 { 1330 synstate_T *p, *prev; 1331 disptick_T tick; 1332 int above; 1333 int dist; 1334 int retval = FALSE; 1335 1336 if (syn_block->b_sst_first == NULL) 1337 return retval; 1338 1339 /* Compute normal distance between non-displayed entries. */ 1340 if (syn_block->b_sst_len <= Rows) 1341 dist = 999999; 1342 else 1343 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1; 1344 1345 /* 1346 * Go through the list to find the "tick" for the oldest entry that can 1347 * be removed. Set "above" when the "tick" for the oldest entry is above 1348 * "b_sst_lasttick" (the display tick wraps around). 1349 */ 1350 tick = syn_block->b_sst_lasttick; 1351 above = FALSE; 1352 prev = syn_block->b_sst_first; 1353 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next) 1354 { 1355 if (prev->sst_lnum + dist > p->sst_lnum) 1356 { 1357 if (p->sst_tick > syn_block->b_sst_lasttick) 1358 { 1359 if (!above || p->sst_tick < tick) 1360 tick = p->sst_tick; 1361 above = TRUE; 1362 } 1363 else if (!above && p->sst_tick < tick) 1364 tick = p->sst_tick; 1365 } 1366 } 1367 1368 /* 1369 * Go through the list to make the entries for the oldest tick at an 1370 * interval of several lines. 1371 */ 1372 prev = syn_block->b_sst_first; 1373 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next) 1374 { 1375 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum) 1376 { 1377 /* Move this entry from used list to free list */ 1378 prev->sst_next = p->sst_next; 1379 syn_stack_free_entry(syn_block, p); 1380 p = prev; 1381 retval = TRUE; 1382 } 1383 } 1384 return retval; 1385 } 1386 1387 /* 1388 * Free the allocated memory for a syn_state item. 1389 * Move the entry into the free list. 1390 */ 1391 static void 1392 syn_stack_free_entry(synblock_T *block, synstate_T *p) 1393 { 1394 clear_syn_state(p); 1395 p->sst_next = block->b_sst_firstfree; 1396 block->b_sst_firstfree = p; 1397 ++block->b_sst_freecount; 1398 } 1399 1400 /* 1401 * Find an entry in the list of state stacks at or before "lnum". 1402 * Returns NULL when there is no entry or the first entry is after "lnum". 1403 */ 1404 static synstate_T * 1405 syn_stack_find_entry(linenr_T lnum) 1406 { 1407 synstate_T *p, *prev; 1408 1409 prev = NULL; 1410 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next) 1411 { 1412 if (p->sst_lnum == lnum) 1413 return p; 1414 if (p->sst_lnum > lnum) 1415 break; 1416 } 1417 return prev; 1418 } 1419 1420 /* 1421 * Try saving the current state in b_sst_array[]. 1422 * The current state must be valid for the start of the current_lnum line! 1423 */ 1424 static synstate_T * 1425 store_current_state(void) 1426 { 1427 int i; 1428 synstate_T *p; 1429 bufstate_T *bp; 1430 stateitem_T *cur_si; 1431 synstate_T *sp = syn_stack_find_entry(current_lnum); 1432 1433 /* 1434 * If the current state contains a start or end pattern that continues 1435 * from the previous line, we can't use it. Don't store it then. 1436 */ 1437 for (i = current_state.ga_len - 1; i >= 0; --i) 1438 { 1439 cur_si = &CUR_STATE(i); 1440 if (cur_si->si_h_startpos.lnum >= current_lnum 1441 || cur_si->si_m_endpos.lnum >= current_lnum 1442 || cur_si->si_h_endpos.lnum >= current_lnum 1443 || (cur_si->si_end_idx 1444 && cur_si->si_eoe_pos.lnum >= current_lnum)) 1445 break; 1446 } 1447 if (i >= 0) 1448 { 1449 if (sp != NULL) 1450 { 1451 /* find "sp" in the list and remove it */ 1452 if (syn_block->b_sst_first == sp) 1453 /* it's the first entry */ 1454 syn_block->b_sst_first = sp->sst_next; 1455 else 1456 { 1457 /* find the entry just before this one to adjust sst_next */ 1458 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next) 1459 if (p->sst_next == sp) 1460 break; 1461 if (p != NULL) /* just in case */ 1462 p->sst_next = sp->sst_next; 1463 } 1464 syn_stack_free_entry(syn_block, sp); 1465 sp = NULL; 1466 } 1467 } 1468 else if (sp == NULL || sp->sst_lnum != current_lnum) 1469 { 1470 /* 1471 * Add a new entry 1472 */ 1473 /* If no free items, cleanup the array first. */ 1474 if (syn_block->b_sst_freecount == 0) 1475 { 1476 (void)syn_stack_cleanup(); 1477 /* "sp" may have been moved to the freelist now */ 1478 sp = syn_stack_find_entry(current_lnum); 1479 } 1480 /* Still no free items? Must be a strange problem... */ 1481 if (syn_block->b_sst_freecount == 0) 1482 sp = NULL; 1483 else 1484 { 1485 /* Take the first item from the free list and put it in the used 1486 * list, after *sp */ 1487 p = syn_block->b_sst_firstfree; 1488 syn_block->b_sst_firstfree = p->sst_next; 1489 --syn_block->b_sst_freecount; 1490 if (sp == NULL) 1491 { 1492 /* Insert in front of the list */ 1493 p->sst_next = syn_block->b_sst_first; 1494 syn_block->b_sst_first = p; 1495 } 1496 else 1497 { 1498 /* insert in list after *sp */ 1499 p->sst_next = sp->sst_next; 1500 sp->sst_next = p; 1501 } 1502 sp = p; 1503 sp->sst_stacksize = 0; 1504 sp->sst_lnum = current_lnum; 1505 } 1506 } 1507 if (sp != NULL) 1508 { 1509 /* When overwriting an existing state stack, clear it first */ 1510 clear_syn_state(sp); 1511 sp->sst_stacksize = current_state.ga_len; 1512 if (current_state.ga_len > SST_FIX_STATES) 1513 { 1514 /* Need to clear it, might be something remaining from when the 1515 * length was less than SST_FIX_STATES. */ 1516 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1); 1517 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL) 1518 sp->sst_stacksize = 0; 1519 else 1520 sp->sst_union.sst_ga.ga_len = current_state.ga_len; 1521 bp = SYN_STATE_P(&(sp->sst_union.sst_ga)); 1522 } 1523 else 1524 bp = sp->sst_union.sst_stack; 1525 for (i = 0; i < sp->sst_stacksize; ++i) 1526 { 1527 bp[i].bs_idx = CUR_STATE(i).si_idx; 1528 bp[i].bs_flags = CUR_STATE(i).si_flags; 1529 #ifdef FEAT_CONCEAL 1530 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr; 1531 bp[i].bs_cchar = CUR_STATE(i).si_cchar; 1532 #endif 1533 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch); 1534 } 1535 sp->sst_next_flags = current_next_flags; 1536 sp->sst_next_list = current_next_list; 1537 sp->sst_tick = display_tick; 1538 sp->sst_change_lnum = 0; 1539 } 1540 current_state_stored = TRUE; 1541 return sp; 1542 } 1543 1544 /* 1545 * Copy a state stack from "from" in b_sst_array[] to current_state; 1546 */ 1547 static void 1548 load_current_state(synstate_T *from) 1549 { 1550 int i; 1551 bufstate_T *bp; 1552 1553 clear_current_state(); 1554 validate_current_state(); 1555 keepend_level = -1; 1556 if (from->sst_stacksize 1557 && ga_grow(¤t_state, from->sst_stacksize) != FAIL) 1558 { 1559 if (from->sst_stacksize > SST_FIX_STATES) 1560 bp = SYN_STATE_P(&(from->sst_union.sst_ga)); 1561 else 1562 bp = from->sst_union.sst_stack; 1563 for (i = 0; i < from->sst_stacksize; ++i) 1564 { 1565 CUR_STATE(i).si_idx = bp[i].bs_idx; 1566 CUR_STATE(i).si_flags = bp[i].bs_flags; 1567 #ifdef FEAT_CONCEAL 1568 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr; 1569 CUR_STATE(i).si_cchar = bp[i].bs_cchar; 1570 #endif 1571 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch); 1572 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND)) 1573 keepend_level = i; 1574 CUR_STATE(i).si_ends = FALSE; 1575 CUR_STATE(i).si_m_lnum = 0; 1576 if (CUR_STATE(i).si_idx >= 0) 1577 CUR_STATE(i).si_next_list = 1578 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list; 1579 else 1580 CUR_STATE(i).si_next_list = NULL; 1581 update_si_attr(i); 1582 } 1583 current_state.ga_len = from->sst_stacksize; 1584 } 1585 current_next_list = from->sst_next_list; 1586 current_next_flags = from->sst_next_flags; 1587 current_lnum = from->sst_lnum; 1588 } 1589 1590 /* 1591 * Compare saved state stack "*sp" with the current state. 1592 * Return TRUE when they are equal. 1593 */ 1594 static int 1595 syn_stack_equal(synstate_T *sp) 1596 { 1597 int i, j; 1598 bufstate_T *bp; 1599 reg_extmatch_T *six, *bsx; 1600 1601 /* First a quick check if the stacks have the same size end nextlist. */ 1602 if (sp->sst_stacksize == current_state.ga_len 1603 && sp->sst_next_list == current_next_list) 1604 { 1605 /* Need to compare all states on both stacks. */ 1606 if (sp->sst_stacksize > SST_FIX_STATES) 1607 bp = SYN_STATE_P(&(sp->sst_union.sst_ga)); 1608 else 1609 bp = sp->sst_union.sst_stack; 1610 1611 for (i = current_state.ga_len; --i >= 0; ) 1612 { 1613 /* If the item has another index the state is different. */ 1614 if (bp[i].bs_idx != CUR_STATE(i).si_idx) 1615 break; 1616 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch) 1617 { 1618 /* When the extmatch pointers are different, the strings in 1619 * them can still be the same. Check if the extmatch 1620 * references are equal. */ 1621 bsx = bp[i].bs_extmatch; 1622 six = CUR_STATE(i).si_extmatch; 1623 /* If one of the extmatch pointers is NULL the states are 1624 * different. */ 1625 if (bsx == NULL || six == NULL) 1626 break; 1627 for (j = 0; j < NSUBEXP; ++j) 1628 { 1629 /* Check each referenced match string. They must all be 1630 * equal. */ 1631 if (bsx->matches[j] != six->matches[j]) 1632 { 1633 /* If the pointer is different it can still be the 1634 * same text. Compare the strings, ignore case when 1635 * the start item has the sp_ic flag set. */ 1636 if (bsx->matches[j] == NULL 1637 || six->matches[j] == NULL) 1638 break; 1639 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic 1640 ? MB_STRICMP(bsx->matches[j], 1641 six->matches[j]) != 0 1642 : STRCMP(bsx->matches[j], six->matches[j]) != 0) 1643 break; 1644 } 1645 } 1646 if (j != NSUBEXP) 1647 break; 1648 } 1649 } 1650 if (i < 0) 1651 return TRUE; 1652 } 1653 return FALSE; 1654 } 1655 1656 /* 1657 * We stop parsing syntax above line "lnum". If the stored state at or below 1658 * this line depended on a change before it, it now depends on the line below 1659 * the last parsed line. 1660 * The window looks like this: 1661 * line which changed 1662 * displayed line 1663 * displayed line 1664 * lnum -> line below window 1665 */ 1666 void 1667 syntax_end_parsing(linenr_T lnum) 1668 { 1669 synstate_T *sp; 1670 1671 sp = syn_stack_find_entry(lnum); 1672 if (sp != NULL && sp->sst_lnum < lnum) 1673 sp = sp->sst_next; 1674 1675 if (sp != NULL && sp->sst_change_lnum != 0) 1676 sp->sst_change_lnum = lnum; 1677 } 1678 1679 /* 1680 * End of handling of the state stack. 1681 ****************************************/ 1682 1683 static void 1684 invalidate_current_state(void) 1685 { 1686 clear_current_state(); 1687 current_state.ga_itemsize = 0; /* mark current_state invalid */ 1688 current_next_list = NULL; 1689 keepend_level = -1; 1690 } 1691 1692 static void 1693 validate_current_state(void) 1694 { 1695 current_state.ga_itemsize = sizeof(stateitem_T); 1696 current_state.ga_growsize = 3; 1697 } 1698 1699 /* 1700 * Return TRUE if the syntax at start of lnum changed since last time. 1701 * This will only be called just after get_syntax_attr() for the previous 1702 * line, to check if the next line needs to be redrawn too. 1703 */ 1704 int 1705 syntax_check_changed(linenr_T lnum) 1706 { 1707 int retval = TRUE; 1708 synstate_T *sp; 1709 1710 /* 1711 * Check the state stack when: 1712 * - lnum is just below the previously syntaxed line. 1713 * - lnum is not before the lines with saved states. 1714 * - lnum is not past the lines with saved states. 1715 * - lnum is at or before the last changed line. 1716 */ 1717 if (VALID_STATE(¤t_state) && lnum == current_lnum + 1) 1718 { 1719 sp = syn_stack_find_entry(lnum); 1720 if (sp != NULL && sp->sst_lnum == lnum) 1721 { 1722 /* 1723 * finish the previous line (needed when not all of the line was 1724 * drawn) 1725 */ 1726 (void)syn_finish_line(FALSE); 1727 1728 /* 1729 * Compare the current state with the previously saved state of 1730 * the line. 1731 */ 1732 if (syn_stack_equal(sp)) 1733 retval = FALSE; 1734 1735 /* 1736 * Store the current state in b_sst_array[] for later use. 1737 */ 1738 ++current_lnum; 1739 (void)store_current_state(); 1740 } 1741 } 1742 1743 return retval; 1744 } 1745 1746 /* 1747 * Finish the current line. 1748 * This doesn't return any attributes, it only gets the state at the end of 1749 * the line. It can start anywhere in the line, as long as the current state 1750 * is valid. 1751 */ 1752 static int 1753 syn_finish_line( 1754 int syncing) /* called for syncing */ 1755 { 1756 stateitem_T *cur_si; 1757 colnr_T prev_current_col; 1758 1759 while (!current_finished) 1760 { 1761 (void)syn_current_attr(syncing, FALSE, NULL, FALSE); 1762 /* 1763 * When syncing, and found some item, need to check the item. 1764 */ 1765 if (syncing && current_state.ga_len) 1766 { 1767 /* 1768 * Check for match with sync item. 1769 */ 1770 cur_si = &CUR_STATE(current_state.ga_len - 1); 1771 if (cur_si->si_idx >= 0 1772 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags 1773 & (HL_SYNC_HERE|HL_SYNC_THERE))) 1774 return TRUE; 1775 1776 /* syn_current_attr() will have skipped the check for an item 1777 * that ends here, need to do that now. Be careful not to go 1778 * past the NUL. */ 1779 prev_current_col = current_col; 1780 if (syn_getcurline()[current_col] != NUL) 1781 ++current_col; 1782 check_state_ends(); 1783 current_col = prev_current_col; 1784 } 1785 ++current_col; 1786 } 1787 return FALSE; 1788 } 1789 1790 /* 1791 * Return highlight attributes for next character. 1792 * Must first call syntax_start() once for the line. 1793 * "col" is normally 0 for the first use in a line, and increments by one each 1794 * time. It's allowed to skip characters and to stop before the end of the 1795 * line. But only a "col" after a previously used column is allowed. 1796 * When "can_spell" is not NULL set it to TRUE when spell-checking should be 1797 * done. 1798 */ 1799 int 1800 get_syntax_attr( 1801 colnr_T col, 1802 int *can_spell, 1803 int keep_state) /* keep state of char at "col" */ 1804 { 1805 int attr = 0; 1806 1807 if (can_spell != NULL) 1808 /* Default: Only do spelling when there is no @Spell cluster or when 1809 * ":syn spell toplevel" was used. */ 1810 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT 1811 ? (syn_block->b_spell_cluster_id == 0) 1812 : (syn_block->b_syn_spell == SYNSPL_TOP); 1813 1814 /* check for out of memory situation */ 1815 if (syn_block->b_sst_array == NULL) 1816 return 0; 1817 1818 /* After 'synmaxcol' the attribute is always zero. */ 1819 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc) 1820 { 1821 clear_current_state(); 1822 #ifdef FEAT_EVAL 1823 current_id = 0; 1824 current_trans_id = 0; 1825 #endif 1826 #ifdef FEAT_CONCEAL 1827 current_flags = 0; 1828 current_seqnr = 0; 1829 #endif 1830 return 0; 1831 } 1832 1833 /* Make sure current_state is valid */ 1834 if (INVALID_STATE(¤t_state)) 1835 validate_current_state(); 1836 1837 /* 1838 * Skip from the current column to "col", get the attributes for "col". 1839 */ 1840 while (current_col <= col) 1841 { 1842 attr = syn_current_attr(FALSE, TRUE, can_spell, 1843 current_col == col ? keep_state : FALSE); 1844 ++current_col; 1845 } 1846 1847 return attr; 1848 } 1849 1850 /* 1851 * Get syntax attributes for current_lnum, current_col. 1852 */ 1853 static int 1854 syn_current_attr( 1855 int syncing, /* When 1: called for syncing */ 1856 int displaying, /* result will be displayed */ 1857 int *can_spell, /* return: do spell checking */ 1858 int keep_state) /* keep syntax stack afterwards */ 1859 { 1860 int syn_id; 1861 lpos_T endpos; /* was: char_u *endp; */ 1862 lpos_T hl_startpos; /* was: int hl_startcol; */ 1863 lpos_T hl_endpos; 1864 lpos_T eos_pos; /* end-of-start match (start region) */ 1865 lpos_T eoe_pos; /* end-of-end pattern */ 1866 int end_idx; /* group ID for end pattern */ 1867 int idx; 1868 synpat_T *spp; 1869 stateitem_T *cur_si, *sip = NULL; 1870 int startcol; 1871 int endcol; 1872 long flags; 1873 int cchar; 1874 short *next_list; 1875 int found_match; /* found usable match */ 1876 static int try_next_column = FALSE; /* must try in next col */ 1877 int do_keywords; 1878 regmmatch_T regmatch; 1879 lpos_T pos; 1880 int lc_col; 1881 reg_extmatch_T *cur_extmatch = NULL; 1882 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */ 1883 char_u *line; /* current line. NOTE: becomes invalid after 1884 looking for a pattern match! */ 1885 1886 /* variables for zero-width matches that have a "nextgroup" argument */ 1887 int keep_next_list; 1888 int zero_width_next_list = FALSE; 1889 garray_T zero_width_next_ga; 1890 1891 /* 1892 * No character, no attributes! Past end of line? 1893 * Do try matching with an empty line (could be the start of a region). 1894 */ 1895 line = syn_getcurline(); 1896 if (line[current_col] == NUL && current_col != 0) 1897 { 1898 /* 1899 * If we found a match after the last column, use it. 1900 */ 1901 if (next_match_idx >= 0 && next_match_col >= (int)current_col 1902 && next_match_col != MAXCOL) 1903 (void)push_next_match(NULL); 1904 1905 current_finished = TRUE; 1906 current_state_stored = FALSE; 1907 return 0; 1908 } 1909 1910 /* if the current or next character is NUL, we will finish the line now */ 1911 if (line[current_col] == NUL || line[current_col + 1] == NUL) 1912 { 1913 current_finished = TRUE; 1914 current_state_stored = FALSE; 1915 } 1916 1917 /* 1918 * When in the previous column there was a match but it could not be used 1919 * (empty match or already matched in this column) need to try again in 1920 * the next column. 1921 */ 1922 if (try_next_column) 1923 { 1924 next_match_idx = -1; 1925 try_next_column = FALSE; 1926 } 1927 1928 /* Only check for keywords when not syncing and there are some. */ 1929 do_keywords = !syncing 1930 && (syn_block->b_keywtab.ht_used > 0 1931 || syn_block->b_keywtab_ic.ht_used > 0); 1932 1933 /* Init the list of zero-width matches with a nextlist. This is used to 1934 * avoid matching the same item in the same position twice. */ 1935 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10); 1936 1937 /* use syntax iskeyword option */ 1938 save_chartab(buf_chartab); 1939 1940 /* 1941 * Repeat matching keywords and patterns, to find contained items at the 1942 * same column. This stops when there are no extra matches at the current 1943 * column. 1944 */ 1945 do 1946 { 1947 found_match = FALSE; 1948 keep_next_list = FALSE; 1949 syn_id = 0; 1950 1951 1952 /* 1953 * 1. Check for a current state. 1954 * Only when there is no current state, or if the current state may 1955 * contain other things, we need to check for keywords and patterns. 1956 * Always need to check for contained items if some item has the 1957 * "containedin" argument (takes extra time!). 1958 */ 1959 if (current_state.ga_len) 1960 cur_si = &CUR_STATE(current_state.ga_len - 1); 1961 else 1962 cur_si = NULL; 1963 1964 if (syn_block->b_syn_containedin || cur_si == NULL 1965 || cur_si->si_cont_list != NULL) 1966 { 1967 /* 1968 * 2. Check for keywords, if on a keyword char after a non-keyword 1969 * char. Don't do this when syncing. 1970 */ 1971 if (do_keywords) 1972 { 1973 line = syn_getcurline(); 1974 if (vim_iswordp_buf(line + current_col, syn_buf) 1975 && (current_col == 0 1976 || !vim_iswordp_buf(line + current_col - 1 1977 - (has_mbyte 1978 ? (*mb_head_off)(line, line + current_col - 1) 1979 : 0) , syn_buf))) 1980 { 1981 syn_id = check_keyword_id(line, (int)current_col, 1982 &endcol, &flags, &next_list, cur_si, 1983 &cchar); 1984 if (syn_id != 0) 1985 { 1986 if (push_current_state(KEYWORD_IDX) == OK) 1987 { 1988 cur_si = &CUR_STATE(current_state.ga_len - 1); 1989 cur_si->si_m_startcol = current_col; 1990 cur_si->si_h_startpos.lnum = current_lnum; 1991 cur_si->si_h_startpos.col = 0; /* starts right away */ 1992 cur_si->si_m_endpos.lnum = current_lnum; 1993 cur_si->si_m_endpos.col = endcol; 1994 cur_si->si_h_endpos.lnum = current_lnum; 1995 cur_si->si_h_endpos.col = endcol; 1996 cur_si->si_ends = TRUE; 1997 cur_si->si_end_idx = 0; 1998 cur_si->si_flags = flags; 1999 #ifdef FEAT_CONCEAL 2000 cur_si->si_seqnr = next_seqnr++; 2001 cur_si->si_cchar = cchar; 2002 if (current_state.ga_len > 1) 2003 cur_si->si_flags |= 2004 CUR_STATE(current_state.ga_len - 2).si_flags 2005 & HL_CONCEAL; 2006 #endif 2007 cur_si->si_id = syn_id; 2008 cur_si->si_trans_id = syn_id; 2009 if (flags & HL_TRANSP) 2010 { 2011 if (current_state.ga_len < 2) 2012 { 2013 cur_si->si_attr = 0; 2014 cur_si->si_trans_id = 0; 2015 } 2016 else 2017 { 2018 cur_si->si_attr = CUR_STATE( 2019 current_state.ga_len - 2).si_attr; 2020 cur_si->si_trans_id = CUR_STATE( 2021 current_state.ga_len - 2).si_trans_id; 2022 } 2023 } 2024 else 2025 cur_si->si_attr = syn_id2attr(syn_id); 2026 cur_si->si_cont_list = NULL; 2027 cur_si->si_next_list = next_list; 2028 check_keepend(); 2029 } 2030 else 2031 vim_free(next_list); 2032 } 2033 } 2034 } 2035 2036 /* 2037 * 3. Check for patterns (only if no keyword found). 2038 */ 2039 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len) 2040 { 2041 /* 2042 * If we didn't check for a match yet, or we are past it, check 2043 * for any match with a pattern. 2044 */ 2045 if (next_match_idx < 0 || next_match_col < (int)current_col) 2046 { 2047 /* 2048 * Check all relevant patterns for a match at this 2049 * position. This is complicated, because matching with a 2050 * pattern takes quite a bit of time, thus we want to 2051 * avoid doing it when it's not needed. 2052 */ 2053 next_match_idx = 0; /* no match in this line yet */ 2054 next_match_col = MAXCOL; 2055 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; ) 2056 { 2057 spp = &(SYN_ITEMS(syn_block)[idx]); 2058 if ( spp->sp_syncing == syncing 2059 && (displaying || !(spp->sp_flags & HL_DISPLAY)) 2060 && (spp->sp_type == SPTYPE_MATCH 2061 || spp->sp_type == SPTYPE_START) 2062 && (current_next_list != NULL 2063 ? in_id_list(NULL, current_next_list, 2064 &spp->sp_syn, 0) 2065 : (cur_si == NULL 2066 ? !(spp->sp_flags & HL_CONTAINED) 2067 : in_id_list(cur_si, 2068 cur_si->si_cont_list, &spp->sp_syn, 2069 spp->sp_flags & HL_CONTAINED)))) 2070 { 2071 int r; 2072 2073 /* If we already tried matching in this line, and 2074 * there isn't a match before next_match_col, skip 2075 * this item. */ 2076 if (spp->sp_line_id == current_line_id 2077 && spp->sp_startcol >= next_match_col) 2078 continue; 2079 spp->sp_line_id = current_line_id; 2080 2081 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF]; 2082 if (lc_col < 0) 2083 lc_col = 0; 2084 2085 regmatch.rmm_ic = spp->sp_ic; 2086 regmatch.regprog = spp->sp_prog; 2087 r = syn_regexec(®match, 2088 current_lnum, 2089 (colnr_T)lc_col, 2090 IF_SYN_TIME(&spp->sp_time)); 2091 spp->sp_prog = regmatch.regprog; 2092 if (!r) 2093 { 2094 /* no match in this line, try another one */ 2095 spp->sp_startcol = MAXCOL; 2096 continue; 2097 } 2098 2099 /* 2100 * Compute the first column of the match. 2101 */ 2102 syn_add_start_off(&pos, ®match, 2103 spp, SPO_MS_OFF, -1); 2104 if (pos.lnum > current_lnum) 2105 { 2106 /* must have used end of match in a next line, 2107 * we can't handle that */ 2108 spp->sp_startcol = MAXCOL; 2109 continue; 2110 } 2111 startcol = pos.col; 2112 2113 /* remember the next column where this pattern 2114 * matches in the current line */ 2115 spp->sp_startcol = startcol; 2116 2117 /* 2118 * If a previously found match starts at a lower 2119 * column number, don't use this one. 2120 */ 2121 if (startcol >= next_match_col) 2122 continue; 2123 2124 /* 2125 * If we matched this pattern at this position 2126 * before, skip it. Must retry in the next 2127 * column, because it may match from there. 2128 */ 2129 if (did_match_already(idx, &zero_width_next_ga)) 2130 { 2131 try_next_column = TRUE; 2132 continue; 2133 } 2134 2135 endpos.lnum = regmatch.endpos[0].lnum; 2136 endpos.col = regmatch.endpos[0].col; 2137 2138 /* Compute the highlight start. */ 2139 syn_add_start_off(&hl_startpos, ®match, 2140 spp, SPO_HS_OFF, -1); 2141 2142 /* Compute the region start. */ 2143 /* Default is to use the end of the match. */ 2144 syn_add_end_off(&eos_pos, ®match, 2145 spp, SPO_RS_OFF, 0); 2146 2147 /* 2148 * Grab the external submatches before they get 2149 * overwritten. Reference count doesn't change. 2150 */ 2151 unref_extmatch(cur_extmatch); 2152 cur_extmatch = re_extmatch_out; 2153 re_extmatch_out = NULL; 2154 2155 flags = 0; 2156 eoe_pos.lnum = 0; /* avoid warning */ 2157 eoe_pos.col = 0; 2158 end_idx = 0; 2159 hl_endpos.lnum = 0; 2160 2161 /* 2162 * For a "oneline" the end must be found in the 2163 * same line too. Search for it after the end of 2164 * the match with the start pattern. Set the 2165 * resulting end positions at the same time. 2166 */ 2167 if (spp->sp_type == SPTYPE_START 2168 && (spp->sp_flags & HL_ONELINE)) 2169 { 2170 lpos_T startpos; 2171 2172 startpos = endpos; 2173 find_endpos(idx, &startpos, &endpos, &hl_endpos, 2174 &flags, &eoe_pos, &end_idx, cur_extmatch); 2175 if (endpos.lnum == 0) 2176 continue; /* not found */ 2177 } 2178 2179 /* 2180 * For a "match" the size must be > 0 after the 2181 * end offset needs has been added. Except when 2182 * syncing. 2183 */ 2184 else if (spp->sp_type == SPTYPE_MATCH) 2185 { 2186 syn_add_end_off(&hl_endpos, ®match, spp, 2187 SPO_HE_OFF, 0); 2188 syn_add_end_off(&endpos, ®match, spp, 2189 SPO_ME_OFF, 0); 2190 if (endpos.lnum == current_lnum 2191 && (int)endpos.col + syncing < startcol) 2192 { 2193 /* 2194 * If an empty string is matched, may need 2195 * to try matching again at next column. 2196 */ 2197 if (regmatch.startpos[0].col 2198 == regmatch.endpos[0].col) 2199 try_next_column = TRUE; 2200 continue; 2201 } 2202 } 2203 2204 /* 2205 * keep the best match so far in next_match_* 2206 */ 2207 /* Highlighting must start after startpos and end 2208 * before endpos. */ 2209 if (hl_startpos.lnum == current_lnum 2210 && (int)hl_startpos.col < startcol) 2211 hl_startpos.col = startcol; 2212 limit_pos_zero(&hl_endpos, &endpos); 2213 2214 next_match_idx = idx; 2215 next_match_col = startcol; 2216 next_match_m_endpos = endpos; 2217 next_match_h_endpos = hl_endpos; 2218 next_match_h_startpos = hl_startpos; 2219 next_match_flags = flags; 2220 next_match_eos_pos = eos_pos; 2221 next_match_eoe_pos = eoe_pos; 2222 next_match_end_idx = end_idx; 2223 unref_extmatch(next_match_extmatch); 2224 next_match_extmatch = cur_extmatch; 2225 cur_extmatch = NULL; 2226 } 2227 } 2228 } 2229 2230 /* 2231 * If we found a match at the current column, use it. 2232 */ 2233 if (next_match_idx >= 0 && next_match_col == (int)current_col) 2234 { 2235 synpat_T *lspp; 2236 2237 /* When a zero-width item matched which has a nextgroup, 2238 * don't push the item but set nextgroup. */ 2239 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]); 2240 if (next_match_m_endpos.lnum == current_lnum 2241 && next_match_m_endpos.col == current_col 2242 && lspp->sp_next_list != NULL) 2243 { 2244 current_next_list = lspp->sp_next_list; 2245 current_next_flags = lspp->sp_flags; 2246 keep_next_list = TRUE; 2247 zero_width_next_list = TRUE; 2248 2249 /* Add the index to a list, so that we can check 2250 * later that we don't match it again (and cause an 2251 * endless loop). */ 2252 if (ga_grow(&zero_width_next_ga, 1) == OK) 2253 { 2254 ((int *)(zero_width_next_ga.ga_data)) 2255 [zero_width_next_ga.ga_len++] = next_match_idx; 2256 } 2257 next_match_idx = -1; 2258 } 2259 else 2260 cur_si = push_next_match(cur_si); 2261 found_match = TRUE; 2262 } 2263 } 2264 } 2265 2266 /* 2267 * Handle searching for nextgroup match. 2268 */ 2269 if (current_next_list != NULL && !keep_next_list) 2270 { 2271 /* 2272 * If a nextgroup was not found, continue looking for one if: 2273 * - this is an empty line and the "skipempty" option was given 2274 * - we are on white space and the "skipwhite" option was given 2275 */ 2276 if (!found_match) 2277 { 2278 line = syn_getcurline(); 2279 if (((current_next_flags & HL_SKIPWHITE) 2280 && VIM_ISWHITE(line[current_col])) 2281 || ((current_next_flags & HL_SKIPEMPTY) 2282 && *line == NUL)) 2283 break; 2284 } 2285 2286 /* 2287 * If a nextgroup was found: Use it, and continue looking for 2288 * contained matches. 2289 * If a nextgroup was not found: Continue looking for a normal 2290 * match. 2291 * When did set current_next_list for a zero-width item and no 2292 * match was found don't loop (would get stuck). 2293 */ 2294 current_next_list = NULL; 2295 next_match_idx = -1; 2296 if (!zero_width_next_list) 2297 found_match = TRUE; 2298 } 2299 2300 } while (found_match); 2301 2302 restore_chartab(buf_chartab); 2303 2304 /* 2305 * Use attributes from the current state, if within its highlighting. 2306 * If not, use attributes from the current-but-one state, etc. 2307 */ 2308 current_attr = 0; 2309 #ifdef FEAT_EVAL 2310 current_id = 0; 2311 current_trans_id = 0; 2312 #endif 2313 #ifdef FEAT_CONCEAL 2314 current_flags = 0; 2315 current_seqnr = 0; 2316 #endif 2317 if (cur_si != NULL) 2318 { 2319 #ifndef FEAT_EVAL 2320 int current_trans_id = 0; 2321 #endif 2322 for (idx = current_state.ga_len - 1; idx >= 0; --idx) 2323 { 2324 sip = &CUR_STATE(idx); 2325 if ((current_lnum > sip->si_h_startpos.lnum 2326 || (current_lnum == sip->si_h_startpos.lnum 2327 && current_col >= sip->si_h_startpos.col)) 2328 && (sip->si_h_endpos.lnum == 0 2329 || current_lnum < sip->si_h_endpos.lnum 2330 || (current_lnum == sip->si_h_endpos.lnum 2331 && current_col < sip->si_h_endpos.col))) 2332 { 2333 current_attr = sip->si_attr; 2334 #ifdef FEAT_EVAL 2335 current_id = sip->si_id; 2336 #endif 2337 current_trans_id = sip->si_trans_id; 2338 #ifdef FEAT_CONCEAL 2339 current_flags = sip->si_flags; 2340 current_seqnr = sip->si_seqnr; 2341 current_sub_char = sip->si_cchar; 2342 #endif 2343 break; 2344 } 2345 } 2346 2347 if (can_spell != NULL) 2348 { 2349 struct sp_syn sps; 2350 2351 /* 2352 * set "can_spell" to TRUE if spell checking is supposed to be 2353 * done in the current item. 2354 */ 2355 if (syn_block->b_spell_cluster_id == 0) 2356 { 2357 /* There is no @Spell cluster: Do spelling for items without 2358 * @NoSpell cluster. */ 2359 if (syn_block->b_nospell_cluster_id == 0 2360 || current_trans_id == 0) 2361 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP); 2362 else 2363 { 2364 sps.inc_tag = 0; 2365 sps.id = syn_block->b_nospell_cluster_id; 2366 sps.cont_in_list = NULL; 2367 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0); 2368 } 2369 } 2370 else 2371 { 2372 /* The @Spell cluster is defined: Do spelling in items with 2373 * the @Spell cluster. But not when @NoSpell is also there. 2374 * At the toplevel only spell check when ":syn spell toplevel" 2375 * was used. */ 2376 if (current_trans_id == 0) 2377 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP); 2378 else 2379 { 2380 sps.inc_tag = 0; 2381 sps.id = syn_block->b_spell_cluster_id; 2382 sps.cont_in_list = NULL; 2383 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0); 2384 2385 if (syn_block->b_nospell_cluster_id != 0) 2386 { 2387 sps.id = syn_block->b_nospell_cluster_id; 2388 if (in_id_list(sip, sip->si_cont_list, &sps, 0)) 2389 *can_spell = FALSE; 2390 } 2391 } 2392 } 2393 } 2394 2395 2396 /* 2397 * Check for end of current state (and the states before it) at the 2398 * next column. Don't do this for syncing, because we would miss a 2399 * single character match. 2400 * First check if the current state ends at the current column. It 2401 * may be for an empty match and a containing item might end in the 2402 * current column. 2403 */ 2404 if (!syncing && !keep_state) 2405 { 2406 check_state_ends(); 2407 if (current_state.ga_len > 0 2408 && syn_getcurline()[current_col] != NUL) 2409 { 2410 ++current_col; 2411 check_state_ends(); 2412 --current_col; 2413 } 2414 } 2415 } 2416 else if (can_spell != NULL) 2417 /* Default: Only do spelling when there is no @Spell cluster or when 2418 * ":syn spell toplevel" was used. */ 2419 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT 2420 ? (syn_block->b_spell_cluster_id == 0) 2421 : (syn_block->b_syn_spell == SYNSPL_TOP); 2422 2423 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */ 2424 if (current_next_list != NULL 2425 && (line = syn_getcurline())[current_col] != NUL 2426 && line[current_col + 1] == NUL 2427 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))) 2428 current_next_list = NULL; 2429 2430 if (zero_width_next_ga.ga_len > 0) 2431 ga_clear(&zero_width_next_ga); 2432 2433 /* No longer need external matches. But keep next_match_extmatch. */ 2434 unref_extmatch(re_extmatch_out); 2435 re_extmatch_out = NULL; 2436 unref_extmatch(cur_extmatch); 2437 2438 return current_attr; 2439 } 2440 2441 2442 /* 2443 * Check if we already matched pattern "idx" at the current column. 2444 */ 2445 static int 2446 did_match_already(int idx, garray_T *gap) 2447 { 2448 int i; 2449 2450 for (i = current_state.ga_len; --i >= 0; ) 2451 if (CUR_STATE(i).si_m_startcol == (int)current_col 2452 && CUR_STATE(i).si_m_lnum == (int)current_lnum 2453 && CUR_STATE(i).si_idx == idx) 2454 return TRUE; 2455 2456 /* Zero-width matches with a nextgroup argument are not put on the syntax 2457 * stack, and can only be matched once anyway. */ 2458 for (i = gap->ga_len; --i >= 0; ) 2459 if (((int *)(gap->ga_data))[i] == idx) 2460 return TRUE; 2461 2462 return FALSE; 2463 } 2464 2465 /* 2466 * Push the next match onto the stack. 2467 */ 2468 static stateitem_T * 2469 push_next_match(stateitem_T *cur_si) 2470 { 2471 synpat_T *spp; 2472 #ifdef FEAT_CONCEAL 2473 int save_flags; 2474 #endif 2475 2476 spp = &(SYN_ITEMS(syn_block)[next_match_idx]); 2477 2478 /* 2479 * Push the item in current_state stack; 2480 */ 2481 if (push_current_state(next_match_idx) == OK) 2482 { 2483 /* 2484 * If it's a start-skip-end type that crosses lines, figure out how 2485 * much it continues in this line. Otherwise just fill in the length. 2486 */ 2487 cur_si = &CUR_STATE(current_state.ga_len - 1); 2488 cur_si->si_h_startpos = next_match_h_startpos; 2489 cur_si->si_m_startcol = current_col; 2490 cur_si->si_m_lnum = current_lnum; 2491 cur_si->si_flags = spp->sp_flags; 2492 #ifdef FEAT_CONCEAL 2493 cur_si->si_seqnr = next_seqnr++; 2494 cur_si->si_cchar = spp->sp_cchar; 2495 if (current_state.ga_len > 1) 2496 cur_si->si_flags |= 2497 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL; 2498 #endif 2499 cur_si->si_next_list = spp->sp_next_list; 2500 cur_si->si_extmatch = ref_extmatch(next_match_extmatch); 2501 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE)) 2502 { 2503 /* Try to find the end pattern in the current line */ 2504 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE); 2505 check_keepend(); 2506 } 2507 else 2508 { 2509 cur_si->si_m_endpos = next_match_m_endpos; 2510 cur_si->si_h_endpos = next_match_h_endpos; 2511 cur_si->si_ends = TRUE; 2512 cur_si->si_flags |= next_match_flags; 2513 cur_si->si_eoe_pos = next_match_eoe_pos; 2514 cur_si->si_end_idx = next_match_end_idx; 2515 } 2516 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND)) 2517 keepend_level = current_state.ga_len - 1; 2518 check_keepend(); 2519 update_si_attr(current_state.ga_len - 1); 2520 2521 #ifdef FEAT_CONCEAL 2522 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS); 2523 #endif 2524 /* 2525 * If the start pattern has another highlight group, push another item 2526 * on the stack for the start pattern. 2527 */ 2528 if ( spp->sp_type == SPTYPE_START 2529 && spp->sp_syn_match_id != 0 2530 && push_current_state(next_match_idx) == OK) 2531 { 2532 cur_si = &CUR_STATE(current_state.ga_len - 1); 2533 cur_si->si_h_startpos = next_match_h_startpos; 2534 cur_si->si_m_startcol = current_col; 2535 cur_si->si_m_lnum = current_lnum; 2536 cur_si->si_m_endpos = next_match_eos_pos; 2537 cur_si->si_h_endpos = next_match_eos_pos; 2538 cur_si->si_ends = TRUE; 2539 cur_si->si_end_idx = 0; 2540 cur_si->si_flags = HL_MATCH; 2541 #ifdef FEAT_CONCEAL 2542 cur_si->si_seqnr = next_seqnr++; 2543 cur_si->si_flags |= save_flags; 2544 if (cur_si->si_flags & HL_CONCEALENDS) 2545 cur_si->si_flags |= HL_CONCEAL; 2546 #endif 2547 cur_si->si_next_list = NULL; 2548 check_keepend(); 2549 update_si_attr(current_state.ga_len - 1); 2550 } 2551 } 2552 2553 next_match_idx = -1; /* try other match next time */ 2554 2555 return cur_si; 2556 } 2557 2558 /* 2559 * Check for end of current state (and the states before it). 2560 */ 2561 static void 2562 check_state_ends(void) 2563 { 2564 stateitem_T *cur_si; 2565 int had_extend; 2566 2567 cur_si = &CUR_STATE(current_state.ga_len - 1); 2568 for (;;) 2569 { 2570 if (cur_si->si_ends 2571 && (cur_si->si_m_endpos.lnum < current_lnum 2572 || (cur_si->si_m_endpos.lnum == current_lnum 2573 && cur_si->si_m_endpos.col <= current_col))) 2574 { 2575 /* 2576 * If there is an end pattern group ID, highlight the end pattern 2577 * now. No need to pop the current item from the stack. 2578 * Only do this if the end pattern continues beyond the current 2579 * position. 2580 */ 2581 if (cur_si->si_end_idx 2582 && (cur_si->si_eoe_pos.lnum > current_lnum 2583 || (cur_si->si_eoe_pos.lnum == current_lnum 2584 && cur_si->si_eoe_pos.col > current_col))) 2585 { 2586 cur_si->si_idx = cur_si->si_end_idx; 2587 cur_si->si_end_idx = 0; 2588 cur_si->si_m_endpos = cur_si->si_eoe_pos; 2589 cur_si->si_h_endpos = cur_si->si_eoe_pos; 2590 cur_si->si_flags |= HL_MATCH; 2591 #ifdef FEAT_CONCEAL 2592 cur_si->si_seqnr = next_seqnr++; 2593 if (cur_si->si_flags & HL_CONCEALENDS) 2594 cur_si->si_flags |= HL_CONCEAL; 2595 #endif 2596 update_si_attr(current_state.ga_len - 1); 2597 2598 /* nextgroup= should not match in the end pattern */ 2599 current_next_list = NULL; 2600 2601 /* what matches next may be different now, clear it */ 2602 next_match_idx = 0; 2603 next_match_col = MAXCOL; 2604 break; 2605 } 2606 else 2607 { 2608 /* handle next_list, unless at end of line and no "skipnl" or 2609 * "skipempty" */ 2610 current_next_list = cur_si->si_next_list; 2611 current_next_flags = cur_si->si_flags; 2612 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)) 2613 && syn_getcurline()[current_col] == NUL) 2614 current_next_list = NULL; 2615 2616 /* When the ended item has "extend", another item with 2617 * "keepend" now needs to check for its end. */ 2618 had_extend = (cur_si->si_flags & HL_EXTEND); 2619 2620 pop_current_state(); 2621 2622 if (current_state.ga_len == 0) 2623 break; 2624 2625 if (had_extend && keepend_level >= 0) 2626 { 2627 syn_update_ends(FALSE); 2628 if (current_state.ga_len == 0) 2629 break; 2630 } 2631 2632 cur_si = &CUR_STATE(current_state.ga_len - 1); 2633 2634 /* 2635 * Only for a region the search for the end continues after 2636 * the end of the contained item. If the contained match 2637 * included the end-of-line, break here, the region continues. 2638 * Don't do this when: 2639 * - "keepend" is used for the contained item 2640 * - not at the end of the line (could be end="x$"me=e-1). 2641 * - "excludenl" is used (HL_HAS_EOL won't be set) 2642 */ 2643 if (cur_si->si_idx >= 0 2644 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type 2645 == SPTYPE_START 2646 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND))) 2647 { 2648 update_si_end(cur_si, (int)current_col, TRUE); 2649 check_keepend(); 2650 if ((current_next_flags & HL_HAS_EOL) 2651 && keepend_level < 0 2652 && syn_getcurline()[current_col] == NUL) 2653 break; 2654 } 2655 } 2656 } 2657 else 2658 break; 2659 } 2660 } 2661 2662 /* 2663 * Update an entry in the current_state stack for a match or region. This 2664 * fills in si_attr, si_next_list and si_cont_list. 2665 */ 2666 static void 2667 update_si_attr(int idx) 2668 { 2669 stateitem_T *sip = &CUR_STATE(idx); 2670 synpat_T *spp; 2671 2672 /* This should not happen... */ 2673 if (sip->si_idx < 0) 2674 return; 2675 2676 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]); 2677 if (sip->si_flags & HL_MATCH) 2678 sip->si_id = spp->sp_syn_match_id; 2679 else 2680 sip->si_id = spp->sp_syn.id; 2681 sip->si_attr = syn_id2attr(sip->si_id); 2682 sip->si_trans_id = sip->si_id; 2683 if (sip->si_flags & HL_MATCH) 2684 sip->si_cont_list = NULL; 2685 else 2686 sip->si_cont_list = spp->sp_cont_list; 2687 2688 /* 2689 * For transparent items, take attr from outer item. 2690 * Also take cont_list, if there is none. 2691 * Don't do this for the matchgroup of a start or end pattern. 2692 */ 2693 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH)) 2694 { 2695 if (idx == 0) 2696 { 2697 sip->si_attr = 0; 2698 sip->si_trans_id = 0; 2699 if (sip->si_cont_list == NULL) 2700 sip->si_cont_list = ID_LIST_ALL; 2701 } 2702 else 2703 { 2704 sip->si_attr = CUR_STATE(idx - 1).si_attr; 2705 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id; 2706 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos; 2707 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos; 2708 if (sip->si_cont_list == NULL) 2709 { 2710 sip->si_flags |= HL_TRANS_CONT; 2711 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list; 2712 } 2713 } 2714 } 2715 } 2716 2717 /* 2718 * Check the current stack for patterns with "keepend" flag. 2719 * Propagate the match-end to contained items, until a "skipend" item is found. 2720 */ 2721 static void 2722 check_keepend(void) 2723 { 2724 int i; 2725 lpos_T maxpos; 2726 lpos_T maxpos_h; 2727 stateitem_T *sip; 2728 2729 /* 2730 * This check can consume a lot of time; only do it from the level where 2731 * there really is a keepend. 2732 */ 2733 if (keepend_level < 0) 2734 return; 2735 2736 /* 2737 * Find the last index of an "extend" item. "keepend" items before that 2738 * won't do anything. If there is no "extend" item "i" will be 2739 * "keepend_level" and all "keepend" items will work normally. 2740 */ 2741 for (i = current_state.ga_len - 1; i > keepend_level; --i) 2742 if (CUR_STATE(i).si_flags & HL_EXTEND) 2743 break; 2744 2745 maxpos.lnum = 0; 2746 maxpos.col = 0; 2747 maxpos_h.lnum = 0; 2748 maxpos_h.col = 0; 2749 for ( ; i < current_state.ga_len; ++i) 2750 { 2751 sip = &CUR_STATE(i); 2752 if (maxpos.lnum != 0) 2753 { 2754 limit_pos_zero(&sip->si_m_endpos, &maxpos); 2755 limit_pos_zero(&sip->si_h_endpos, &maxpos_h); 2756 limit_pos_zero(&sip->si_eoe_pos, &maxpos); 2757 sip->si_ends = TRUE; 2758 } 2759 if (sip->si_ends && (sip->si_flags & HL_KEEPEND)) 2760 { 2761 if (maxpos.lnum == 0 2762 || maxpos.lnum > sip->si_m_endpos.lnum 2763 || (maxpos.lnum == sip->si_m_endpos.lnum 2764 && maxpos.col > sip->si_m_endpos.col)) 2765 maxpos = sip->si_m_endpos; 2766 if (maxpos_h.lnum == 0 2767 || maxpos_h.lnum > sip->si_h_endpos.lnum 2768 || (maxpos_h.lnum == sip->si_h_endpos.lnum 2769 && maxpos_h.col > sip->si_h_endpos.col)) 2770 maxpos_h = sip->si_h_endpos; 2771 } 2772 } 2773 } 2774 2775 /* 2776 * Update an entry in the current_state stack for a start-skip-end pattern. 2777 * This finds the end of the current item, if it's in the current line. 2778 * 2779 * Return the flags for the matched END. 2780 */ 2781 static void 2782 update_si_end( 2783 stateitem_T *sip, 2784 int startcol, /* where to start searching for the end */ 2785 int force) /* when TRUE overrule a previous end */ 2786 { 2787 lpos_T startpos; 2788 lpos_T endpos; 2789 lpos_T hl_endpos; 2790 lpos_T end_endpos; 2791 int end_idx; 2792 2793 /* return quickly for a keyword */ 2794 if (sip->si_idx < 0) 2795 return; 2796 2797 /* Don't update when it's already done. Can be a match of an end pattern 2798 * that started in a previous line. Watch out: can also be a "keepend" 2799 * from a containing item. */ 2800 if (!force && sip->si_m_endpos.lnum >= current_lnum) 2801 return; 2802 2803 /* 2804 * We need to find the end of the region. It may continue in the next 2805 * line. 2806 */ 2807 end_idx = 0; 2808 startpos.lnum = current_lnum; 2809 startpos.col = startcol; 2810 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos, 2811 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch); 2812 2813 if (endpos.lnum == 0) 2814 { 2815 /* No end pattern matched. */ 2816 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE) 2817 { 2818 /* a "oneline" never continues in the next line */ 2819 sip->si_ends = TRUE; 2820 sip->si_m_endpos.lnum = current_lnum; 2821 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline()); 2822 } 2823 else 2824 { 2825 /* continues in the next line */ 2826 sip->si_ends = FALSE; 2827 sip->si_m_endpos.lnum = 0; 2828 } 2829 sip->si_h_endpos = sip->si_m_endpos; 2830 } 2831 else 2832 { 2833 /* match within this line */ 2834 sip->si_m_endpos = endpos; 2835 sip->si_h_endpos = hl_endpos; 2836 sip->si_eoe_pos = end_endpos; 2837 sip->si_ends = TRUE; 2838 sip->si_end_idx = end_idx; 2839 } 2840 } 2841 2842 /* 2843 * Add a new state to the current state stack. 2844 * It is cleared and the index set to "idx". 2845 * Return FAIL if it's not possible (out of memory). 2846 */ 2847 static int 2848 push_current_state(int idx) 2849 { 2850 if (ga_grow(¤t_state, 1) == FAIL) 2851 return FAIL; 2852 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T)); 2853 CUR_STATE(current_state.ga_len).si_idx = idx; 2854 ++current_state.ga_len; 2855 return OK; 2856 } 2857 2858 /* 2859 * Remove a state from the current_state stack. 2860 */ 2861 static void 2862 pop_current_state(void) 2863 { 2864 if (current_state.ga_len) 2865 { 2866 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch); 2867 --current_state.ga_len; 2868 } 2869 /* after the end of a pattern, try matching a keyword or pattern */ 2870 next_match_idx = -1; 2871 2872 /* if first state with "keepend" is popped, reset keepend_level */ 2873 if (keepend_level >= current_state.ga_len) 2874 keepend_level = -1; 2875 } 2876 2877 /* 2878 * Find the end of a start/skip/end syntax region after "startpos". 2879 * Only checks one line. 2880 * Also handles a match item that continued from a previous line. 2881 * If not found, the syntax item continues in the next line. m_endpos->lnum 2882 * will be 0. 2883 * If found, the end of the region and the end of the highlighting is 2884 * computed. 2885 */ 2886 static void 2887 find_endpos( 2888 int idx, /* index of the pattern */ 2889 lpos_T *startpos, /* where to start looking for an END match */ 2890 lpos_T *m_endpos, /* return: end of match */ 2891 lpos_T *hl_endpos, /* return: end of highlighting */ 2892 long *flagsp, /* return: flags of matching END */ 2893 lpos_T *end_endpos, /* return: end of end pattern match */ 2894 int *end_idx, /* return: group ID for end pat. match, or 0 */ 2895 reg_extmatch_T *start_ext) /* submatches from the start pattern */ 2896 { 2897 colnr_T matchcol; 2898 synpat_T *spp, *spp_skip; 2899 int start_idx; 2900 int best_idx; 2901 regmmatch_T regmatch; 2902 regmmatch_T best_regmatch; /* startpos/endpos of best match */ 2903 lpos_T pos; 2904 char_u *line; 2905 int had_match = FALSE; 2906 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */ 2907 2908 /* just in case we are invoked for a keyword */ 2909 if (idx < 0) 2910 return; 2911 2912 /* 2913 * Check for being called with a START pattern. 2914 * Can happen with a match that continues to the next line, because it 2915 * contained a region. 2916 */ 2917 spp = &(SYN_ITEMS(syn_block)[idx]); 2918 if (spp->sp_type != SPTYPE_START) 2919 { 2920 *hl_endpos = *startpos; 2921 return; 2922 } 2923 2924 /* 2925 * Find the SKIP or first END pattern after the last START pattern. 2926 */ 2927 for (;;) 2928 { 2929 spp = &(SYN_ITEMS(syn_block)[idx]); 2930 if (spp->sp_type != SPTYPE_START) 2931 break; 2932 ++idx; 2933 } 2934 2935 /* 2936 * Lookup the SKIP pattern (if present) 2937 */ 2938 if (spp->sp_type == SPTYPE_SKIP) 2939 { 2940 spp_skip = spp; 2941 ++idx; 2942 } 2943 else 2944 spp_skip = NULL; 2945 2946 /* Setup external matches for syn_regexec(). */ 2947 unref_extmatch(re_extmatch_in); 2948 re_extmatch_in = ref_extmatch(start_ext); 2949 2950 matchcol = startpos->col; /* start looking for a match at sstart */ 2951 start_idx = idx; /* remember the first END pattern. */ 2952 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */ 2953 2954 /* use syntax iskeyword option */ 2955 save_chartab(buf_chartab); 2956 2957 for (;;) 2958 { 2959 /* 2960 * Find end pattern that matches first after "matchcol". 2961 */ 2962 best_idx = -1; 2963 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx) 2964 { 2965 int lc_col = matchcol; 2966 int r; 2967 2968 spp = &(SYN_ITEMS(syn_block)[idx]); 2969 if (spp->sp_type != SPTYPE_END) /* past last END pattern */ 2970 break; 2971 lc_col -= spp->sp_offsets[SPO_LC_OFF]; 2972 if (lc_col < 0) 2973 lc_col = 0; 2974 2975 regmatch.rmm_ic = spp->sp_ic; 2976 regmatch.regprog = spp->sp_prog; 2977 r = syn_regexec(®match, startpos->lnum, lc_col, 2978 IF_SYN_TIME(&spp->sp_time)); 2979 spp->sp_prog = regmatch.regprog; 2980 if (r) 2981 { 2982 if (best_idx == -1 || regmatch.startpos[0].col 2983 < best_regmatch.startpos[0].col) 2984 { 2985 best_idx = idx; 2986 best_regmatch.startpos[0] = regmatch.startpos[0]; 2987 best_regmatch.endpos[0] = regmatch.endpos[0]; 2988 } 2989 } 2990 } 2991 2992 /* 2993 * If all end patterns have been tried, and there is no match, the 2994 * item continues until end-of-line. 2995 */ 2996 if (best_idx == -1) 2997 break; 2998 2999 /* 3000 * If the skip pattern matches before the end pattern, 3001 * continue searching after the skip pattern. 3002 */ 3003 if (spp_skip != NULL) 3004 { 3005 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF]; 3006 int r; 3007 3008 if (lc_col < 0) 3009 lc_col = 0; 3010 regmatch.rmm_ic = spp_skip->sp_ic; 3011 regmatch.regprog = spp_skip->sp_prog; 3012 r = syn_regexec(®match, startpos->lnum, lc_col, 3013 IF_SYN_TIME(&spp_skip->sp_time)); 3014 spp_skip->sp_prog = regmatch.regprog; 3015 if (r && regmatch.startpos[0].col 3016 <= best_regmatch.startpos[0].col) 3017 { 3018 int line_len; 3019 3020 /* Add offset to skip pattern match */ 3021 syn_add_end_off(&pos, ®match, spp_skip, SPO_ME_OFF, 1); 3022 3023 /* If the skip pattern goes on to the next line, there is no 3024 * match with an end pattern in this line. */ 3025 if (pos.lnum > startpos->lnum) 3026 break; 3027 3028 line = ml_get_buf(syn_buf, startpos->lnum, FALSE); 3029 line_len = (int)STRLEN(line); 3030 3031 /* take care of an empty match or negative offset */ 3032 if (pos.col <= matchcol) 3033 ++matchcol; 3034 else if (pos.col <= regmatch.endpos[0].col) 3035 matchcol = pos.col; 3036 else 3037 /* Be careful not to jump over the NUL at the end-of-line */ 3038 for (matchcol = regmatch.endpos[0].col; 3039 matchcol < line_len && matchcol < pos.col; 3040 ++matchcol) 3041 ; 3042 3043 /* if the skip pattern includes end-of-line, break here */ 3044 if (matchcol >= line_len) 3045 break; 3046 3047 continue; /* start with first end pattern again */ 3048 } 3049 } 3050 3051 /* 3052 * Match from start pattern to end pattern. 3053 * Correct for match and highlight offset of end pattern. 3054 */ 3055 spp = &(SYN_ITEMS(syn_block)[best_idx]); 3056 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1); 3057 /* can't end before the start */ 3058 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col) 3059 m_endpos->col = startpos->col; 3060 3061 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1); 3062 /* can't end before the start */ 3063 if (end_endpos->lnum == startpos->lnum 3064 && end_endpos->col < startpos->col) 3065 end_endpos->col = startpos->col; 3066 /* can't end after the match */ 3067 limit_pos(end_endpos, m_endpos); 3068 3069 /* 3070 * If the end group is highlighted differently, adjust the pointers. 3071 */ 3072 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0) 3073 { 3074 *end_idx = best_idx; 3075 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT))) 3076 { 3077 hl_endpos->lnum = best_regmatch.endpos[0].lnum; 3078 hl_endpos->col = best_regmatch.endpos[0].col; 3079 } 3080 else 3081 { 3082 hl_endpos->lnum = best_regmatch.startpos[0].lnum; 3083 hl_endpos->col = best_regmatch.startpos[0].col; 3084 } 3085 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF]; 3086 3087 /* can't end before the start */ 3088 if (hl_endpos->lnum == startpos->lnum 3089 && hl_endpos->col < startpos->col) 3090 hl_endpos->col = startpos->col; 3091 limit_pos(hl_endpos, m_endpos); 3092 3093 /* now the match ends where the highlighting ends, it is turned 3094 * into the matchgroup for the end */ 3095 *m_endpos = *hl_endpos; 3096 } 3097 else 3098 { 3099 *end_idx = 0; 3100 *hl_endpos = *end_endpos; 3101 } 3102 3103 *flagsp = spp->sp_flags; 3104 3105 had_match = TRUE; 3106 break; 3107 } 3108 3109 /* no match for an END pattern in this line */ 3110 if (!had_match) 3111 m_endpos->lnum = 0; 3112 3113 restore_chartab(buf_chartab); 3114 3115 /* Remove external matches. */ 3116 unref_extmatch(re_extmatch_in); 3117 re_extmatch_in = NULL; 3118 } 3119 3120 /* 3121 * Limit "pos" not to be after "limit". 3122 */ 3123 static void 3124 limit_pos(lpos_T *pos, lpos_T *limit) 3125 { 3126 if (pos->lnum > limit->lnum) 3127 *pos = *limit; 3128 else if (pos->lnum == limit->lnum && pos->col > limit->col) 3129 pos->col = limit->col; 3130 } 3131 3132 /* 3133 * Limit "pos" not to be after "limit", unless pos->lnum is zero. 3134 */ 3135 static void 3136 limit_pos_zero( 3137 lpos_T *pos, 3138 lpos_T *limit) 3139 { 3140 if (pos->lnum == 0) 3141 *pos = *limit; 3142 else 3143 limit_pos(pos, limit); 3144 } 3145 3146 /* 3147 * Add offset to matched text for end of match or highlight. 3148 */ 3149 static void 3150 syn_add_end_off( 3151 lpos_T *result, /* returned position */ 3152 regmmatch_T *regmatch, /* start/end of match */ 3153 synpat_T *spp, /* matched pattern */ 3154 int idx, /* index of offset */ 3155 int extra) /* extra chars for offset to start */ 3156 { 3157 int col; 3158 int off; 3159 char_u *base; 3160 char_u *p; 3161 3162 if (spp->sp_off_flags & (1 << idx)) 3163 { 3164 result->lnum = regmatch->startpos[0].lnum; 3165 col = regmatch->startpos[0].col; 3166 off = spp->sp_offsets[idx] + extra; 3167 } 3168 else 3169 { 3170 result->lnum = regmatch->endpos[0].lnum; 3171 col = regmatch->endpos[0].col; 3172 off = spp->sp_offsets[idx]; 3173 } 3174 /* Don't go past the end of the line. Matters for "rs=e+2" when there 3175 * is a matchgroup. Watch out for match with last NL in the buffer. */ 3176 if (result->lnum > syn_buf->b_ml.ml_line_count) 3177 col = 0; 3178 else if (off != 0) 3179 { 3180 base = ml_get_buf(syn_buf, result->lnum, FALSE); 3181 p = base + col; 3182 if (off > 0) 3183 { 3184 while (off-- > 0 && *p != NUL) 3185 MB_PTR_ADV(p); 3186 } 3187 else if (off < 0) 3188 { 3189 while (off++ < 0 && base < p) 3190 MB_PTR_BACK(base, p); 3191 } 3192 col = (int)(p - base); 3193 } 3194 result->col = col; 3195 } 3196 3197 /* 3198 * Add offset to matched text for start of match or highlight. 3199 * Avoid resulting column to become negative. 3200 */ 3201 static void 3202 syn_add_start_off( 3203 lpos_T *result, /* returned position */ 3204 regmmatch_T *regmatch, /* start/end of match */ 3205 synpat_T *spp, 3206 int idx, 3207 int extra) /* extra chars for offset to end */ 3208 { 3209 int col; 3210 int off; 3211 char_u *base; 3212 char_u *p; 3213 3214 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT))) 3215 { 3216 result->lnum = regmatch->endpos[0].lnum; 3217 col = regmatch->endpos[0].col; 3218 off = spp->sp_offsets[idx] + extra; 3219 } 3220 else 3221 { 3222 result->lnum = regmatch->startpos[0].lnum; 3223 col = regmatch->startpos[0].col; 3224 off = spp->sp_offsets[idx]; 3225 } 3226 if (result->lnum > syn_buf->b_ml.ml_line_count) 3227 { 3228 /* a "\n" at the end of the pattern may take us below the last line */ 3229 result->lnum = syn_buf->b_ml.ml_line_count; 3230 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE)); 3231 } 3232 if (off != 0) 3233 { 3234 base = ml_get_buf(syn_buf, result->lnum, FALSE); 3235 p = base + col; 3236 if (off > 0) 3237 { 3238 while (off-- && *p != NUL) 3239 MB_PTR_ADV(p); 3240 } 3241 else if (off < 0) 3242 { 3243 while (off++ && base < p) 3244 MB_PTR_BACK(base, p); 3245 } 3246 col = (int)(p - base); 3247 } 3248 result->col = col; 3249 } 3250 3251 /* 3252 * Get current line in syntax buffer. 3253 */ 3254 static char_u * 3255 syn_getcurline(void) 3256 { 3257 return ml_get_buf(syn_buf, current_lnum, FALSE); 3258 } 3259 3260 /* 3261 * Call vim_regexec() to find a match with "rmp" in "syn_buf". 3262 * Returns TRUE when there is a match. 3263 */ 3264 static int 3265 syn_regexec( 3266 regmmatch_T *rmp, 3267 linenr_T lnum, 3268 colnr_T col, 3269 syn_time_T *st UNUSED) 3270 { 3271 int r; 3272 #ifdef FEAT_RELTIME 3273 int timed_out = FALSE; 3274 #endif 3275 #ifdef FEAT_PROFILE 3276 proftime_T pt; 3277 3278 if (syn_time_on) 3279 profile_start(&pt); 3280 #endif 3281 3282 if (rmp->regprog == NULL) 3283 // This can happen if a previous call to vim_regexec_multi() tried to 3284 // use the NFA engine, which resulted in NFA_TOO_EXPENSIVE, and 3285 // compiling the pattern with the other engine fails. 3286 return FALSE; 3287 3288 rmp->rmm_maxcol = syn_buf->b_p_smc; 3289 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, 3290 #ifdef FEAT_RELTIME 3291 syn_tm, &timed_out 3292 #else 3293 NULL, NULL 3294 #endif 3295 ); 3296 3297 #ifdef FEAT_PROFILE 3298 if (syn_time_on) 3299 { 3300 profile_end(&pt); 3301 profile_add(&st->total, &pt); 3302 if (profile_cmp(&pt, &st->slowest) < 0) 3303 st->slowest = pt; 3304 ++st->count; 3305 if (r > 0) 3306 ++st->match; 3307 } 3308 #endif 3309 #ifdef FEAT_RELTIME 3310 if (timed_out && !syn_win->w_s->b_syn_slow) 3311 { 3312 syn_win->w_s->b_syn_slow = TRUE; 3313 msg(_("'redrawtime' exceeded, syntax highlighting disabled")); 3314 } 3315 #endif 3316 3317 if (r > 0) 3318 { 3319 rmp->startpos[0].lnum += lnum; 3320 rmp->endpos[0].lnum += lnum; 3321 return TRUE; 3322 } 3323 return FALSE; 3324 } 3325 3326 /* 3327 * Check one position in a line for a matching keyword. 3328 * The caller must check if a keyword can start at startcol. 3329 * Return its ID if found, 0 otherwise. 3330 */ 3331 static int 3332 check_keyword_id( 3333 char_u *line, 3334 int startcol, /* position in line to check for keyword */ 3335 int *endcolp, /* return: character after found keyword */ 3336 long *flagsp, /* return: flags of matching keyword */ 3337 short **next_listp, /* return: next_list of matching keyword */ 3338 stateitem_T *cur_si, /* item at the top of the stack */ 3339 int *ccharp UNUSED) /* conceal substitution char */ 3340 { 3341 keyentry_T *kp; 3342 char_u *kwp; 3343 int round; 3344 int kwlen; 3345 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */ 3346 hashtab_T *ht; 3347 hashitem_T *hi; 3348 3349 /* Find first character after the keyword. First character was already 3350 * checked. */ 3351 kwp = line + startcol; 3352 kwlen = 0; 3353 do 3354 { 3355 if (has_mbyte) 3356 kwlen += (*mb_ptr2len)(kwp + kwlen); 3357 else 3358 ++kwlen; 3359 } 3360 while (vim_iswordp_buf(kwp + kwlen, syn_buf)); 3361 3362 if (kwlen > MAXKEYWLEN) 3363 return 0; 3364 3365 /* 3366 * Must make a copy of the keyword, so we can add a NUL and make it 3367 * lowercase. 3368 */ 3369 vim_strncpy(keyword, kwp, kwlen); 3370 3371 /* 3372 * Try twice: 3373 * 1. matching case 3374 * 2. ignoring case 3375 */ 3376 for (round = 1; round <= 2; ++round) 3377 { 3378 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic; 3379 if (ht->ht_used == 0) 3380 continue; 3381 if (round == 2) /* ignore case */ 3382 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1); 3383 3384 /* 3385 * Find keywords that match. There can be several with different 3386 * attributes. 3387 * When current_next_list is non-zero accept only that group, otherwise: 3388 * Accept a not-contained keyword at toplevel. 3389 * Accept a keyword at other levels only if it is in the contains list. 3390 */ 3391 hi = hash_find(ht, keyword); 3392 if (!HASHITEM_EMPTY(hi)) 3393 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next) 3394 { 3395 if (current_next_list != 0 3396 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0) 3397 : (cur_si == NULL 3398 ? !(kp->flags & HL_CONTAINED) 3399 : in_id_list(cur_si, cur_si->si_cont_list, 3400 &kp->k_syn, kp->flags & HL_CONTAINED))) 3401 { 3402 *endcolp = startcol + kwlen; 3403 *flagsp = kp->flags; 3404 *next_listp = kp->next_list; 3405 #ifdef FEAT_CONCEAL 3406 *ccharp = kp->k_char; 3407 #endif 3408 return kp->k_syn.id; 3409 } 3410 } 3411 } 3412 return 0; 3413 } 3414 3415 /* 3416 * Handle ":syntax conceal" command. 3417 */ 3418 static void 3419 syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED) 3420 { 3421 #ifdef FEAT_CONCEAL 3422 char_u *arg = eap->arg; 3423 char_u *next; 3424 3425 eap->nextcmd = find_nextcmd(arg); 3426 if (eap->skip) 3427 return; 3428 3429 next = skiptowhite(arg); 3430 if (*arg == NUL) 3431 { 3432 if (curwin->w_s->b_syn_conceal) 3433 msg(_("syntax conceal on")); 3434 else 3435 msg(_("syntax conceal off")); 3436 } 3437 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2) 3438 curwin->w_s->b_syn_conceal = TRUE; 3439 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3) 3440 curwin->w_s->b_syn_conceal = FALSE; 3441 else 3442 semsg(_("E390: Illegal argument: %s"), arg); 3443 #endif 3444 } 3445 3446 /* 3447 * Handle ":syntax case" command. 3448 */ 3449 static void 3450 syn_cmd_case(exarg_T *eap, int syncing UNUSED) 3451 { 3452 char_u *arg = eap->arg; 3453 char_u *next; 3454 3455 eap->nextcmd = find_nextcmd(arg); 3456 if (eap->skip) 3457 return; 3458 3459 next = skiptowhite(arg); 3460 if (*arg == NUL) 3461 { 3462 if (curwin->w_s->b_syn_ic) 3463 msg(_("syntax case ignore")); 3464 else 3465 msg(_("syntax case match")); 3466 } 3467 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5) 3468 curwin->w_s->b_syn_ic = FALSE; 3469 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6) 3470 curwin->w_s->b_syn_ic = TRUE; 3471 else 3472 semsg(_("E390: Illegal argument: %s"), arg); 3473 } 3474 3475 /* 3476 * Handle ":syntax spell" command. 3477 */ 3478 static void 3479 syn_cmd_spell(exarg_T *eap, int syncing UNUSED) 3480 { 3481 char_u *arg = eap->arg; 3482 char_u *next; 3483 3484 eap->nextcmd = find_nextcmd(arg); 3485 if (eap->skip) 3486 return; 3487 3488 next = skiptowhite(arg); 3489 if (*arg == NUL) 3490 { 3491 if (curwin->w_s->b_syn_spell == SYNSPL_TOP) 3492 msg(_("syntax spell toplevel")); 3493 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP) 3494 msg(_("syntax spell notoplevel")); 3495 else 3496 msg(_("syntax spell default")); 3497 } 3498 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8) 3499 curwin->w_s->b_syn_spell = SYNSPL_TOP; 3500 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10) 3501 curwin->w_s->b_syn_spell = SYNSPL_NOTOP; 3502 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7) 3503 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT; 3504 else 3505 { 3506 semsg(_("E390: Illegal argument: %s"), arg); 3507 return; 3508 } 3509 3510 /* assume spell checking changed, force a redraw */ 3511 redraw_win_later(curwin, NOT_VALID); 3512 } 3513 3514 /* 3515 * Handle ":syntax iskeyword" command. 3516 */ 3517 static void 3518 syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED) 3519 { 3520 char_u *arg = eap->arg; 3521 char_u save_chartab[32]; 3522 char_u *save_isk; 3523 3524 if (eap->skip) 3525 return; 3526 3527 arg = skipwhite(arg); 3528 if (*arg == NUL) 3529 { 3530 msg_puts("\n"); 3531 if (curwin->w_s->b_syn_isk != empty_option) 3532 { 3533 msg_puts(_("syntax iskeyword ")); 3534 msg_outtrans(curwin->w_s->b_syn_isk); 3535 } 3536 else 3537 msg_outtrans((char_u *)_("syntax iskeyword not set")); 3538 } 3539 else 3540 { 3541 if (STRNICMP(arg, "clear", 5) == 0) 3542 { 3543 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab, 3544 (size_t)32); 3545 clear_string_option(&curwin->w_s->b_syn_isk); 3546 } 3547 else 3548 { 3549 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32); 3550 save_isk = curbuf->b_p_isk; 3551 curbuf->b_p_isk = vim_strsave(arg); 3552 3553 buf_init_chartab(curbuf, FALSE); 3554 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab, 3555 (size_t)32); 3556 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32); 3557 clear_string_option(&curwin->w_s->b_syn_isk); 3558 curwin->w_s->b_syn_isk = curbuf->b_p_isk; 3559 curbuf->b_p_isk = save_isk; 3560 } 3561 } 3562 redraw_win_later(curwin, NOT_VALID); 3563 } 3564 3565 /* 3566 * Clear all syntax info for one buffer. 3567 */ 3568 void 3569 syntax_clear(synblock_T *block) 3570 { 3571 int i; 3572 3573 block->b_syn_error = FALSE; /* clear previous error */ 3574 #ifdef FEAT_RELTIME 3575 block->b_syn_slow = FALSE; /* clear previous timeout */ 3576 #endif 3577 block->b_syn_ic = FALSE; /* Use case, by default */ 3578 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */ 3579 block->b_syn_containedin = FALSE; 3580 #ifdef FEAT_CONCEAL 3581 block->b_syn_conceal = FALSE; 3582 #endif 3583 3584 /* free the keywords */ 3585 clear_keywtab(&block->b_keywtab); 3586 clear_keywtab(&block->b_keywtab_ic); 3587 3588 /* free the syntax patterns */ 3589 for (i = block->b_syn_patterns.ga_len; --i >= 0; ) 3590 syn_clear_pattern(block, i); 3591 ga_clear(&block->b_syn_patterns); 3592 3593 /* free the syntax clusters */ 3594 for (i = block->b_syn_clusters.ga_len; --i >= 0; ) 3595 syn_clear_cluster(block, i); 3596 ga_clear(&block->b_syn_clusters); 3597 block->b_spell_cluster_id = 0; 3598 block->b_nospell_cluster_id = 0; 3599 3600 block->b_syn_sync_flags = 0; 3601 block->b_syn_sync_minlines = 0; 3602 block->b_syn_sync_maxlines = 0; 3603 block->b_syn_sync_linebreaks = 0; 3604 3605 vim_regfree(block->b_syn_linecont_prog); 3606 block->b_syn_linecont_prog = NULL; 3607 VIM_CLEAR(block->b_syn_linecont_pat); 3608 #ifdef FEAT_FOLDING 3609 block->b_syn_folditems = 0; 3610 #endif 3611 clear_string_option(&block->b_syn_isk); 3612 3613 /* free the stored states */ 3614 syn_stack_free_all(block); 3615 invalidate_current_state(); 3616 3617 /* Reset the counter for ":syn include" */ 3618 running_syn_inc_tag = 0; 3619 } 3620 3621 /* 3622 * Get rid of ownsyntax for window "wp". 3623 */ 3624 void 3625 reset_synblock(win_T *wp) 3626 { 3627 if (wp->w_s != &wp->w_buffer->b_s) 3628 { 3629 syntax_clear(wp->w_s); 3630 vim_free(wp->w_s); 3631 wp->w_s = &wp->w_buffer->b_s; 3632 } 3633 } 3634 3635 /* 3636 * Clear syncing info for one buffer. 3637 */ 3638 static void 3639 syntax_sync_clear(void) 3640 { 3641 int i; 3642 3643 /* free the syntax patterns */ 3644 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; ) 3645 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing) 3646 syn_remove_pattern(curwin->w_s, i); 3647 3648 curwin->w_s->b_syn_sync_flags = 0; 3649 curwin->w_s->b_syn_sync_minlines = 0; 3650 curwin->w_s->b_syn_sync_maxlines = 0; 3651 curwin->w_s->b_syn_sync_linebreaks = 0; 3652 3653 vim_regfree(curwin->w_s->b_syn_linecont_prog); 3654 curwin->w_s->b_syn_linecont_prog = NULL; 3655 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat); 3656 clear_string_option(&curwin->w_s->b_syn_isk); 3657 3658 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ 3659 } 3660 3661 /* 3662 * Remove one pattern from the buffer's pattern list. 3663 */ 3664 static void 3665 syn_remove_pattern( 3666 synblock_T *block, 3667 int idx) 3668 { 3669 synpat_T *spp; 3670 3671 spp = &(SYN_ITEMS(block)[idx]); 3672 #ifdef FEAT_FOLDING 3673 if (spp->sp_flags & HL_FOLD) 3674 --block->b_syn_folditems; 3675 #endif 3676 syn_clear_pattern(block, idx); 3677 mch_memmove(spp, spp + 1, 3678 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1)); 3679 --block->b_syn_patterns.ga_len; 3680 } 3681 3682 /* 3683 * Clear and free one syntax pattern. When clearing all, must be called from 3684 * last to first! 3685 */ 3686 static void 3687 syn_clear_pattern(synblock_T *block, int i) 3688 { 3689 vim_free(SYN_ITEMS(block)[i].sp_pattern); 3690 vim_regfree(SYN_ITEMS(block)[i].sp_prog); 3691 /* Only free sp_cont_list and sp_next_list of first start pattern */ 3692 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START) 3693 { 3694 vim_free(SYN_ITEMS(block)[i].sp_cont_list); 3695 vim_free(SYN_ITEMS(block)[i].sp_next_list); 3696 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list); 3697 } 3698 } 3699 3700 /* 3701 * Clear and free one syntax cluster. 3702 */ 3703 static void 3704 syn_clear_cluster(synblock_T *block, int i) 3705 { 3706 vim_free(SYN_CLSTR(block)[i].scl_name); 3707 vim_free(SYN_CLSTR(block)[i].scl_name_u); 3708 vim_free(SYN_CLSTR(block)[i].scl_list); 3709 } 3710 3711 /* 3712 * Handle ":syntax clear" command. 3713 */ 3714 static void 3715 syn_cmd_clear(exarg_T *eap, int syncing) 3716 { 3717 char_u *arg = eap->arg; 3718 char_u *arg_end; 3719 int id; 3720 3721 eap->nextcmd = find_nextcmd(arg); 3722 if (eap->skip) 3723 return; 3724 3725 /* 3726 * We have to disable this within ":syn include @group filename", 3727 * because otherwise @group would get deleted. 3728 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn 3729 * clear". 3730 */ 3731 if (curwin->w_s->b_syn_topgrp != 0) 3732 return; 3733 3734 if (ends_excmd(*arg)) 3735 { 3736 /* 3737 * No argument: Clear all syntax items. 3738 */ 3739 if (syncing) 3740 syntax_sync_clear(); 3741 else 3742 { 3743 syntax_clear(curwin->w_s); 3744 if (curwin->w_s == &curwin->w_buffer->b_s) 3745 do_unlet((char_u *)"b:current_syntax", TRUE); 3746 do_unlet((char_u *)"w:current_syntax", TRUE); 3747 } 3748 } 3749 else 3750 { 3751 /* 3752 * Clear the group IDs that are in the argument. 3753 */ 3754 while (!ends_excmd(*arg)) 3755 { 3756 arg_end = skiptowhite(arg); 3757 if (*arg == '@') 3758 { 3759 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1)); 3760 if (id == 0) 3761 { 3762 semsg(_("E391: No such syntax cluster: %s"), arg); 3763 break; 3764 } 3765 else 3766 { 3767 /* 3768 * We can't physically delete a cluster without changing 3769 * the IDs of other clusters, so we do the next best thing 3770 * and make it empty. 3771 */ 3772 short scl_id = id - SYNID_CLUSTER; 3773 3774 VIM_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list); 3775 } 3776 } 3777 else 3778 { 3779 id = syn_namen2id(arg, (int)(arg_end - arg)); 3780 if (id == 0) 3781 { 3782 semsg(_(e_nogroup), arg); 3783 break; 3784 } 3785 else 3786 syn_clear_one(id, syncing); 3787 } 3788 arg = skipwhite(arg_end); 3789 } 3790 } 3791 redraw_curbuf_later(SOME_VALID); 3792 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ 3793 } 3794 3795 /* 3796 * Clear one syntax group for the current buffer. 3797 */ 3798 static void 3799 syn_clear_one(int id, int syncing) 3800 { 3801 synpat_T *spp; 3802 int idx; 3803 3804 /* Clear keywords only when not ":syn sync clear group-name" */ 3805 if (!syncing) 3806 { 3807 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab); 3808 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic); 3809 } 3810 3811 /* clear the patterns for "id" */ 3812 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; ) 3813 { 3814 spp = &(SYN_ITEMS(curwin->w_s)[idx]); 3815 if (spp->sp_syn.id != id || spp->sp_syncing != syncing) 3816 continue; 3817 syn_remove_pattern(curwin->w_s, idx); 3818 } 3819 } 3820 3821 /* 3822 * Handle ":syntax on" command. 3823 */ 3824 static void 3825 syn_cmd_on(exarg_T *eap, int syncing UNUSED) 3826 { 3827 syn_cmd_onoff(eap, "syntax"); 3828 } 3829 3830 /* 3831 * Handle ":syntax enable" command. 3832 */ 3833 static void 3834 syn_cmd_enable(exarg_T *eap, int syncing UNUSED) 3835 { 3836 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable"); 3837 syn_cmd_onoff(eap, "syntax"); 3838 do_unlet((char_u *)"g:syntax_cmd", TRUE); 3839 } 3840 3841 /* 3842 * Handle ":syntax reset" command. 3843 * It actually resets highlighting, not syntax. 3844 */ 3845 static void 3846 syn_cmd_reset(exarg_T *eap, int syncing UNUSED) 3847 { 3848 eap->nextcmd = check_nextcmd(eap->arg); 3849 if (!eap->skip) 3850 { 3851 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset"); 3852 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim"); 3853 do_unlet((char_u *)"g:syntax_cmd", TRUE); 3854 } 3855 } 3856 3857 /* 3858 * Handle ":syntax manual" command. 3859 */ 3860 static void 3861 syn_cmd_manual(exarg_T *eap, int syncing UNUSED) 3862 { 3863 syn_cmd_onoff(eap, "manual"); 3864 } 3865 3866 /* 3867 * Handle ":syntax off" command. 3868 */ 3869 static void 3870 syn_cmd_off(exarg_T *eap, int syncing UNUSED) 3871 { 3872 syn_cmd_onoff(eap, "nosyntax"); 3873 } 3874 3875 static void 3876 syn_cmd_onoff(exarg_T *eap, char *name) 3877 { 3878 char_u buf[100]; 3879 3880 eap->nextcmd = check_nextcmd(eap->arg); 3881 if (!eap->skip) 3882 { 3883 STRCPY(buf, "so "); 3884 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name); 3885 do_cmdline_cmd(buf); 3886 } 3887 } 3888 3889 /* 3890 * Handle ":syntax [list]" command: list current syntax words. 3891 */ 3892 static void 3893 syn_cmd_list( 3894 exarg_T *eap, 3895 int syncing) /* when TRUE: list syncing items */ 3896 { 3897 char_u *arg = eap->arg; 3898 int id; 3899 char_u *arg_end; 3900 3901 eap->nextcmd = find_nextcmd(arg); 3902 if (eap->skip) 3903 return; 3904 3905 if (!syntax_present(curwin)) 3906 { 3907 msg(_(msg_no_items)); 3908 return; 3909 } 3910 3911 if (syncing) 3912 { 3913 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT) 3914 { 3915 msg_puts(_("syncing on C-style comments")); 3916 syn_lines_msg(); 3917 syn_match_msg(); 3918 return; 3919 } 3920 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH)) 3921 { 3922 if (curwin->w_s->b_syn_sync_minlines == 0) 3923 msg_puts(_("no syncing")); 3924 else 3925 { 3926 msg_puts(_("syncing starts ")); 3927 msg_outnum(curwin->w_s->b_syn_sync_minlines); 3928 msg_puts(_(" lines before top line")); 3929 syn_match_msg(); 3930 } 3931 return; 3932 } 3933 msg_puts_title(_("\n--- Syntax sync items ---")); 3934 if (curwin->w_s->b_syn_sync_minlines > 0 3935 || curwin->w_s->b_syn_sync_maxlines > 0 3936 || curwin->w_s->b_syn_sync_linebreaks > 0) 3937 { 3938 msg_puts(_("\nsyncing on items")); 3939 syn_lines_msg(); 3940 syn_match_msg(); 3941 } 3942 } 3943 else 3944 msg_puts_title(_("\n--- Syntax items ---")); 3945 if (ends_excmd(*arg)) 3946 { 3947 /* 3948 * No argument: List all group IDs and all syntax clusters. 3949 */ 3950 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id) 3951 syn_list_one(id, syncing, FALSE); 3952 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id) 3953 syn_list_cluster(id); 3954 } 3955 else 3956 { 3957 /* 3958 * List the group IDs and syntax clusters that are in the argument. 3959 */ 3960 while (!ends_excmd(*arg) && !got_int) 3961 { 3962 arg_end = skiptowhite(arg); 3963 if (*arg == '@') 3964 { 3965 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1)); 3966 if (id == 0) 3967 semsg(_("E392: No such syntax cluster: %s"), arg); 3968 else 3969 syn_list_cluster(id - SYNID_CLUSTER); 3970 } 3971 else 3972 { 3973 id = syn_namen2id(arg, (int)(arg_end - arg)); 3974 if (id == 0) 3975 semsg(_(e_nogroup), arg); 3976 else 3977 syn_list_one(id, syncing, TRUE); 3978 } 3979 arg = skipwhite(arg_end); 3980 } 3981 } 3982 eap->nextcmd = check_nextcmd(arg); 3983 } 3984 3985 static void 3986 syn_lines_msg(void) 3987 { 3988 if (curwin->w_s->b_syn_sync_maxlines > 0 3989 || curwin->w_s->b_syn_sync_minlines > 0) 3990 { 3991 msg_puts("; "); 3992 if (curwin->w_s->b_syn_sync_minlines > 0) 3993 { 3994 msg_puts(_("minimal ")); 3995 msg_outnum(curwin->w_s->b_syn_sync_minlines); 3996 if (curwin->w_s->b_syn_sync_maxlines) 3997 msg_puts(", "); 3998 } 3999 if (curwin->w_s->b_syn_sync_maxlines > 0) 4000 { 4001 msg_puts(_("maximal ")); 4002 msg_outnum(curwin->w_s->b_syn_sync_maxlines); 4003 } 4004 msg_puts(_(" lines before top line")); 4005 } 4006 } 4007 4008 static void 4009 syn_match_msg(void) 4010 { 4011 if (curwin->w_s->b_syn_sync_linebreaks > 0) 4012 { 4013 msg_puts(_("; match ")); 4014 msg_outnum(curwin->w_s->b_syn_sync_linebreaks); 4015 msg_puts(_(" line breaks")); 4016 } 4017 } 4018 4019 static int last_matchgroup; 4020 4021 struct name_list 4022 { 4023 int flag; 4024 char *name; 4025 }; 4026 4027 static void syn_list_flags(struct name_list *nl, int flags, int attr); 4028 4029 /* 4030 * List one syntax item, for ":syntax" or "syntax list syntax_name". 4031 */ 4032 static void 4033 syn_list_one( 4034 int id, 4035 int syncing, /* when TRUE: list syncing items */ 4036 int link_only) /* when TRUE; list link-only too */ 4037 { 4038 int attr; 4039 int idx; 4040 int did_header = FALSE; 4041 synpat_T *spp; 4042 static struct name_list namelist1[] = 4043 { 4044 {HL_DISPLAY, "display"}, 4045 {HL_CONTAINED, "contained"}, 4046 {HL_ONELINE, "oneline"}, 4047 {HL_KEEPEND, "keepend"}, 4048 {HL_EXTEND, "extend"}, 4049 {HL_EXCLUDENL, "excludenl"}, 4050 {HL_TRANSP, "transparent"}, 4051 {HL_FOLD, "fold"}, 4052 #ifdef FEAT_CONCEAL 4053 {HL_CONCEAL, "conceal"}, 4054 {HL_CONCEALENDS, "concealends"}, 4055 #endif 4056 {0, NULL} 4057 }; 4058 static struct name_list namelist2[] = 4059 { 4060 {HL_SKIPWHITE, "skipwhite"}, 4061 {HL_SKIPNL, "skipnl"}, 4062 {HL_SKIPEMPTY, "skipempty"}, 4063 {0, NULL} 4064 }; 4065 4066 attr = HL_ATTR(HLF_D); /* highlight like directories */ 4067 4068 /* list the keywords for "id" */ 4069 if (!syncing) 4070 { 4071 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr); 4072 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic, 4073 did_header, attr); 4074 } 4075 4076 /* list the patterns for "id" */ 4077 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx) 4078 { 4079 spp = &(SYN_ITEMS(curwin->w_s)[idx]); 4080 if (spp->sp_syn.id != id || spp->sp_syncing != syncing) 4081 continue; 4082 4083 (void)syn_list_header(did_header, 999, id); 4084 did_header = TRUE; 4085 last_matchgroup = 0; 4086 if (spp->sp_type == SPTYPE_MATCH) 4087 { 4088 put_pattern("match", ' ', spp, attr); 4089 msg_putchar(' '); 4090 } 4091 else if (spp->sp_type == SPTYPE_START) 4092 { 4093 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START) 4094 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr); 4095 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP) 4096 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr); 4097 while (idx < curwin->w_s->b_syn_patterns.ga_len 4098 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END) 4099 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr); 4100 --idx; 4101 msg_putchar(' '); 4102 } 4103 syn_list_flags(namelist1, spp->sp_flags, attr); 4104 4105 if (spp->sp_cont_list != NULL) 4106 put_id_list((char_u *)"contains", spp->sp_cont_list, attr); 4107 4108 if (spp->sp_syn.cont_in_list != NULL) 4109 put_id_list((char_u *)"containedin", 4110 spp->sp_syn.cont_in_list, attr); 4111 4112 if (spp->sp_next_list != NULL) 4113 { 4114 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr); 4115 syn_list_flags(namelist2, spp->sp_flags, attr); 4116 } 4117 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE)) 4118 { 4119 if (spp->sp_flags & HL_SYNC_HERE) 4120 msg_puts_attr("grouphere", attr); 4121 else 4122 msg_puts_attr("groupthere", attr); 4123 msg_putchar(' '); 4124 if (spp->sp_sync_idx >= 0) 4125 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s) 4126 [spp->sp_sync_idx].sp_syn.id - 1].sg_name); 4127 else 4128 msg_puts("NONE"); 4129 msg_putchar(' '); 4130 } 4131 } 4132 4133 /* list the link, if there is one */ 4134 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int) 4135 { 4136 (void)syn_list_header(did_header, 999, id); 4137 msg_puts_attr("links to", attr); 4138 msg_putchar(' '); 4139 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name); 4140 } 4141 } 4142 4143 static void 4144 syn_list_flags(struct name_list *nlist, int flags, int attr) 4145 { 4146 int i; 4147 4148 for (i = 0; nlist[i].flag != 0; ++i) 4149 if (flags & nlist[i].flag) 4150 { 4151 msg_puts_attr(nlist[i].name, attr); 4152 msg_putchar(' '); 4153 } 4154 } 4155 4156 /* 4157 * List one syntax cluster, for ":syntax" or "syntax list syntax_name". 4158 */ 4159 static void 4160 syn_list_cluster(int id) 4161 { 4162 int endcol = 15; 4163 4164 /* slight hack: roughly duplicate the guts of syn_list_header() */ 4165 msg_putchar('\n'); 4166 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name); 4167 4168 if (msg_col >= endcol) /* output at least one space */ 4169 endcol = msg_col + 1; 4170 if (Columns <= endcol) /* avoid hang for tiny window */ 4171 endcol = Columns - 1; 4172 4173 msg_advance(endcol); 4174 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL) 4175 { 4176 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list, 4177 HL_ATTR(HLF_D)); 4178 } 4179 else 4180 { 4181 msg_puts_attr("cluster", HL_ATTR(HLF_D)); 4182 msg_puts("=NONE"); 4183 } 4184 } 4185 4186 static void 4187 put_id_list(char_u *name, short *list, int attr) 4188 { 4189 short *p; 4190 4191 msg_puts_attr((char *)name, attr); 4192 msg_putchar('='); 4193 for (p = list; *p; ++p) 4194 { 4195 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP) 4196 { 4197 if (p[1]) 4198 msg_puts("ALLBUT"); 4199 else 4200 msg_puts("ALL"); 4201 } 4202 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED) 4203 { 4204 msg_puts("TOP"); 4205 } 4206 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER) 4207 { 4208 msg_puts("CONTAINED"); 4209 } 4210 else if (*p >= SYNID_CLUSTER) 4211 { 4212 short scl_id = *p - SYNID_CLUSTER; 4213 4214 msg_putchar('@'); 4215 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name); 4216 } 4217 else 4218 msg_outtrans(HL_TABLE()[*p - 1].sg_name); 4219 if (p[1]) 4220 msg_putchar(','); 4221 } 4222 msg_putchar(' '); 4223 } 4224 4225 static void 4226 put_pattern( 4227 char *s, 4228 int c, 4229 synpat_T *spp, 4230 int attr) 4231 { 4232 long n; 4233 int mask; 4234 int first; 4235 static char *sepchars = "/+=-#@\"|'^&"; 4236 int i; 4237 4238 /* May have to write "matchgroup=group" */ 4239 if (last_matchgroup != spp->sp_syn_match_id) 4240 { 4241 last_matchgroup = spp->sp_syn_match_id; 4242 msg_puts_attr("matchgroup", attr); 4243 msg_putchar('='); 4244 if (last_matchgroup == 0) 4245 msg_outtrans((char_u *)"NONE"); 4246 else 4247 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name); 4248 msg_putchar(' '); 4249 } 4250 4251 /* output the name of the pattern and an '=' or ' ' */ 4252 msg_puts_attr(s, attr); 4253 msg_putchar(c); 4254 4255 /* output the pattern, in between a char that is not in the pattern */ 4256 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; ) 4257 if (sepchars[++i] == NUL) 4258 { 4259 i = 0; /* no good char found, just use the first one */ 4260 break; 4261 } 4262 msg_putchar(sepchars[i]); 4263 msg_outtrans(spp->sp_pattern); 4264 msg_putchar(sepchars[i]); 4265 4266 /* output any pattern options */ 4267 first = TRUE; 4268 for (i = 0; i < SPO_COUNT; ++i) 4269 { 4270 mask = (1 << i); 4271 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT))) 4272 { 4273 if (!first) 4274 msg_putchar(','); /* separate with commas */ 4275 msg_puts(spo_name_tab[i]); 4276 n = spp->sp_offsets[i]; 4277 if (i != SPO_LC_OFF) 4278 { 4279 if (spp->sp_off_flags & mask) 4280 msg_putchar('s'); 4281 else 4282 msg_putchar('e'); 4283 if (n > 0) 4284 msg_putchar('+'); 4285 } 4286 if (n || i == SPO_LC_OFF) 4287 msg_outnum(n); 4288 first = FALSE; 4289 } 4290 } 4291 msg_putchar(' '); 4292 } 4293 4294 /* 4295 * List or clear the keywords for one syntax group. 4296 * Return TRUE if the header has been printed. 4297 */ 4298 static int 4299 syn_list_keywords( 4300 int id, 4301 hashtab_T *ht, 4302 int did_header, /* header has already been printed */ 4303 int attr) 4304 { 4305 int outlen; 4306 hashitem_T *hi; 4307 keyentry_T *kp; 4308 int todo; 4309 int prev_contained = 0; 4310 short *prev_next_list = NULL; 4311 short *prev_cont_in_list = NULL; 4312 int prev_skipnl = 0; 4313 int prev_skipwhite = 0; 4314 int prev_skipempty = 0; 4315 4316 /* 4317 * Unfortunately, this list of keywords is not sorted on alphabet but on 4318 * hash value... 4319 */ 4320 todo = (int)ht->ht_used; 4321 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi) 4322 { 4323 if (!HASHITEM_EMPTY(hi)) 4324 { 4325 --todo; 4326 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next) 4327 { 4328 if (kp->k_syn.id == id) 4329 { 4330 if (prev_contained != (kp->flags & HL_CONTAINED) 4331 || prev_skipnl != (kp->flags & HL_SKIPNL) 4332 || prev_skipwhite != (kp->flags & HL_SKIPWHITE) 4333 || prev_skipempty != (kp->flags & HL_SKIPEMPTY) 4334 || prev_cont_in_list != kp->k_syn.cont_in_list 4335 || prev_next_list != kp->next_list) 4336 outlen = 9999; 4337 else 4338 outlen = (int)STRLEN(kp->keyword); 4339 /* output "contained" and "nextgroup" on each line */ 4340 if (syn_list_header(did_header, outlen, id)) 4341 { 4342 prev_contained = 0; 4343 prev_next_list = NULL; 4344 prev_cont_in_list = NULL; 4345 prev_skipnl = 0; 4346 prev_skipwhite = 0; 4347 prev_skipempty = 0; 4348 } 4349 did_header = TRUE; 4350 if (prev_contained != (kp->flags & HL_CONTAINED)) 4351 { 4352 msg_puts_attr("contained", attr); 4353 msg_putchar(' '); 4354 prev_contained = (kp->flags & HL_CONTAINED); 4355 } 4356 if (kp->k_syn.cont_in_list != prev_cont_in_list) 4357 { 4358 put_id_list((char_u *)"containedin", 4359 kp->k_syn.cont_in_list, attr); 4360 msg_putchar(' '); 4361 prev_cont_in_list = kp->k_syn.cont_in_list; 4362 } 4363 if (kp->next_list != prev_next_list) 4364 { 4365 put_id_list((char_u *)"nextgroup", kp->next_list, attr); 4366 msg_putchar(' '); 4367 prev_next_list = kp->next_list; 4368 if (kp->flags & HL_SKIPNL) 4369 { 4370 msg_puts_attr("skipnl", attr); 4371 msg_putchar(' '); 4372 prev_skipnl = (kp->flags & HL_SKIPNL); 4373 } 4374 if (kp->flags & HL_SKIPWHITE) 4375 { 4376 msg_puts_attr("skipwhite", attr); 4377 msg_putchar(' '); 4378 prev_skipwhite = (kp->flags & HL_SKIPWHITE); 4379 } 4380 if (kp->flags & HL_SKIPEMPTY) 4381 { 4382 msg_puts_attr("skipempty", attr); 4383 msg_putchar(' '); 4384 prev_skipempty = (kp->flags & HL_SKIPEMPTY); 4385 } 4386 } 4387 msg_outtrans(kp->keyword); 4388 } 4389 } 4390 } 4391 } 4392 4393 return did_header; 4394 } 4395 4396 static void 4397 syn_clear_keyword(int id, hashtab_T *ht) 4398 { 4399 hashitem_T *hi; 4400 keyentry_T *kp; 4401 keyentry_T *kp_prev; 4402 keyentry_T *kp_next; 4403 int todo; 4404 4405 hash_lock(ht); 4406 todo = (int)ht->ht_used; 4407 for (hi = ht->ht_array; todo > 0; ++hi) 4408 { 4409 if (!HASHITEM_EMPTY(hi)) 4410 { 4411 --todo; 4412 kp_prev = NULL; 4413 for (kp = HI2KE(hi); kp != NULL; ) 4414 { 4415 if (kp->k_syn.id == id) 4416 { 4417 kp_next = kp->ke_next; 4418 if (kp_prev == NULL) 4419 { 4420 if (kp_next == NULL) 4421 hash_remove(ht, hi); 4422 else 4423 hi->hi_key = KE2HIKEY(kp_next); 4424 } 4425 else 4426 kp_prev->ke_next = kp_next; 4427 vim_free(kp->next_list); 4428 vim_free(kp->k_syn.cont_in_list); 4429 vim_free(kp); 4430 kp = kp_next; 4431 } 4432 else 4433 { 4434 kp_prev = kp; 4435 kp = kp->ke_next; 4436 } 4437 } 4438 } 4439 } 4440 hash_unlock(ht); 4441 } 4442 4443 /* 4444 * Clear a whole keyword table. 4445 */ 4446 static void 4447 clear_keywtab(hashtab_T *ht) 4448 { 4449 hashitem_T *hi; 4450 int todo; 4451 keyentry_T *kp; 4452 keyentry_T *kp_next; 4453 4454 todo = (int)ht->ht_used; 4455 for (hi = ht->ht_array; todo > 0; ++hi) 4456 { 4457 if (!HASHITEM_EMPTY(hi)) 4458 { 4459 --todo; 4460 for (kp = HI2KE(hi); kp != NULL; kp = kp_next) 4461 { 4462 kp_next = kp->ke_next; 4463 vim_free(kp->next_list); 4464 vim_free(kp->k_syn.cont_in_list); 4465 vim_free(kp); 4466 } 4467 } 4468 } 4469 hash_clear(ht); 4470 hash_init(ht); 4471 } 4472 4473 /* 4474 * Add a keyword to the list of keywords. 4475 */ 4476 static void 4477 add_keyword( 4478 char_u *name, /* name of keyword */ 4479 int id, /* group ID for this keyword */ 4480 int flags, /* flags for this keyword */ 4481 short *cont_in_list, /* containedin for this keyword */ 4482 short *next_list, /* nextgroup for this keyword */ 4483 int conceal_char) 4484 { 4485 keyentry_T *kp; 4486 hashtab_T *ht; 4487 hashitem_T *hi; 4488 char_u *name_ic; 4489 long_u hash; 4490 char_u name_folded[MAXKEYWLEN + 1]; 4491 4492 if (curwin->w_s->b_syn_ic) 4493 name_ic = str_foldcase(name, (int)STRLEN(name), 4494 name_folded, MAXKEYWLEN + 1); 4495 else 4496 name_ic = name; 4497 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic))); 4498 if (kp == NULL) 4499 return; 4500 STRCPY(kp->keyword, name_ic); 4501 kp->k_syn.id = id; 4502 kp->k_syn.inc_tag = current_syn_inc_tag; 4503 kp->flags = flags; 4504 kp->k_char = conceal_char; 4505 kp->k_syn.cont_in_list = copy_id_list(cont_in_list); 4506 if (cont_in_list != NULL) 4507 curwin->w_s->b_syn_containedin = TRUE; 4508 kp->next_list = copy_id_list(next_list); 4509 4510 if (curwin->w_s->b_syn_ic) 4511 ht = &curwin->w_s->b_keywtab_ic; 4512 else 4513 ht = &curwin->w_s->b_keywtab; 4514 4515 hash = hash_hash(kp->keyword); 4516 hi = hash_lookup(ht, kp->keyword, hash); 4517 if (HASHITEM_EMPTY(hi)) 4518 { 4519 /* new keyword, add to hashtable */ 4520 kp->ke_next = NULL; 4521 hash_add_item(ht, hi, kp->keyword, hash); 4522 } 4523 else 4524 { 4525 /* keyword already exists, prepend to list */ 4526 kp->ke_next = HI2KE(hi); 4527 hi->hi_key = KE2HIKEY(kp); 4528 } 4529 } 4530 4531 /* 4532 * Get the start and end of the group name argument. 4533 * Return a pointer to the first argument. 4534 * Return NULL if the end of the command was found instead of further args. 4535 */ 4536 static char_u * 4537 get_group_name( 4538 char_u *arg, /* start of the argument */ 4539 char_u **name_end) /* pointer to end of the name */ 4540 { 4541 char_u *rest; 4542 4543 *name_end = skiptowhite(arg); 4544 rest = skipwhite(*name_end); 4545 4546 /* 4547 * Check if there are enough arguments. The first argument may be a 4548 * pattern, where '|' is allowed, so only check for NUL. 4549 */ 4550 if (ends_excmd(*arg) || *rest == NUL) 4551 return NULL; 4552 return rest; 4553 } 4554 4555 /* 4556 * Check for syntax command option arguments. 4557 * This can be called at any place in the list of arguments, and just picks 4558 * out the arguments that are known. Can be called several times in a row to 4559 * collect all options in between other arguments. 4560 * Return a pointer to the next argument (which isn't an option). 4561 * Return NULL for any error; 4562 */ 4563 static char_u * 4564 get_syn_options( 4565 char_u *arg, /* next argument to be checked */ 4566 syn_opt_arg_T *opt, /* various things */ 4567 int *conceal_char UNUSED, 4568 int skip) /* TRUE if skipping over command */ 4569 { 4570 char_u *gname_start, *gname; 4571 int syn_id; 4572 int len; 4573 char *p; 4574 int i; 4575 int fidx; 4576 static struct flag 4577 { 4578 char *name; 4579 int argtype; 4580 int flags; 4581 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED}, 4582 {"oOnNeElLiInNeE", 0, HL_ONELINE}, 4583 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND}, 4584 {"eExXtTeEnNdD", 0, HL_EXTEND}, 4585 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL}, 4586 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP}, 4587 {"sSkKiIpPnNlL", 0, HL_SKIPNL}, 4588 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE}, 4589 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY}, 4590 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE}, 4591 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE}, 4592 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY}, 4593 {"fFoOlLdD", 0, HL_FOLD}, 4594 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL}, 4595 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS}, 4596 {"cCcChHaArR", 11, 0}, 4597 {"cCoOnNtTaAiInNsS", 1, 0}, 4598 {"cCoOnNtTaAiInNeEdDiInN", 2, 0}, 4599 {"nNeExXtTgGrRoOuUpP", 3, 0}, 4600 }; 4601 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN"; 4602 4603 if (arg == NULL) /* already detected error */ 4604 return NULL; 4605 4606 #ifdef FEAT_CONCEAL 4607 if (curwin->w_s->b_syn_conceal) 4608 opt->flags |= HL_CONCEAL; 4609 #endif 4610 4611 for (;;) 4612 { 4613 /* 4614 * This is used very often when a large number of keywords is defined. 4615 * Need to skip quickly when no option name is found. 4616 * Also avoid tolower(), it's slow. 4617 */ 4618 if (strchr(first_letters, *arg) == NULL) 4619 break; 4620 4621 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; ) 4622 { 4623 p = flagtab[fidx].name; 4624 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len) 4625 if (arg[len] != p[i] && arg[len] != p[i + 1]) 4626 break; 4627 if (p[i] == NUL && (VIM_ISWHITE(arg[len]) 4628 || (flagtab[fidx].argtype > 0 4629 ? arg[len] == '=' 4630 : ends_excmd(arg[len])))) 4631 { 4632 if (opt->keyword 4633 && (flagtab[fidx].flags == HL_DISPLAY 4634 || flagtab[fidx].flags == HL_FOLD 4635 || flagtab[fidx].flags == HL_EXTEND)) 4636 /* treat "display", "fold" and "extend" as a keyword */ 4637 fidx = -1; 4638 break; 4639 } 4640 } 4641 if (fidx < 0) /* no match found */ 4642 break; 4643 4644 if (flagtab[fidx].argtype == 1) 4645 { 4646 if (!opt->has_cont_list) 4647 { 4648 emsg(_("E395: contains argument not accepted here")); 4649 return NULL; 4650 } 4651 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL) 4652 return NULL; 4653 } 4654 else if (flagtab[fidx].argtype == 2) 4655 { 4656 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL) 4657 return NULL; 4658 } 4659 else if (flagtab[fidx].argtype == 3) 4660 { 4661 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL) 4662 return NULL; 4663 } 4664 else if (flagtab[fidx].argtype == 11 && arg[5] == '=') 4665 { 4666 /* cchar=? */ 4667 if (has_mbyte) 4668 { 4669 #ifdef FEAT_CONCEAL 4670 *conceal_char = mb_ptr2char(arg + 6); 4671 #endif 4672 arg += mb_ptr2len(arg + 6) - 1; 4673 } 4674 else 4675 { 4676 #ifdef FEAT_CONCEAL 4677 *conceal_char = arg[6]; 4678 #else 4679 ; 4680 #endif 4681 } 4682 #ifdef FEAT_CONCEAL 4683 if (!vim_isprintc_strict(*conceal_char)) 4684 { 4685 emsg(_("E844: invalid cchar value")); 4686 return NULL; 4687 } 4688 #endif 4689 arg = skipwhite(arg + 7); 4690 } 4691 else 4692 { 4693 opt->flags |= flagtab[fidx].flags; 4694 arg = skipwhite(arg + len); 4695 4696 if (flagtab[fidx].flags == HL_SYNC_HERE 4697 || flagtab[fidx].flags == HL_SYNC_THERE) 4698 { 4699 if (opt->sync_idx == NULL) 4700 { 4701 emsg(_("E393: group[t]here not accepted here")); 4702 return NULL; 4703 } 4704 gname_start = arg; 4705 arg = skiptowhite(arg); 4706 if (gname_start == arg) 4707 return NULL; 4708 gname = vim_strnsave(gname_start, (int)(arg - gname_start)); 4709 if (gname == NULL) 4710 return NULL; 4711 if (STRCMP(gname, "NONE") == 0) 4712 *opt->sync_idx = NONE_IDX; 4713 else 4714 { 4715 syn_id = syn_name2id(gname); 4716 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; ) 4717 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id 4718 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START) 4719 { 4720 *opt->sync_idx = i; 4721 break; 4722 } 4723 if (i < 0) 4724 { 4725 semsg(_("E394: Didn't find region item for %s"), gname); 4726 vim_free(gname); 4727 return NULL; 4728 } 4729 } 4730 4731 vim_free(gname); 4732 arg = skipwhite(arg); 4733 } 4734 #ifdef FEAT_FOLDING 4735 else if (flagtab[fidx].flags == HL_FOLD 4736 && foldmethodIsSyntax(curwin)) 4737 /* Need to update folds later. */ 4738 foldUpdateAll(curwin); 4739 #endif 4740 } 4741 } 4742 4743 return arg; 4744 } 4745 4746 /* 4747 * Adjustments to syntax item when declared in a ":syn include"'d file. 4748 * Set the contained flag, and if the item is not already contained, add it 4749 * to the specified top-level group, if any. 4750 */ 4751 static void 4752 syn_incl_toplevel(int id, int *flagsp) 4753 { 4754 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0) 4755 return; 4756 *flagsp |= HL_CONTAINED; 4757 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER) 4758 { 4759 /* We have to alloc this, because syn_combine_list() will free it. */ 4760 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short))); 4761 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER; 4762 4763 if (grp_list != NULL) 4764 { 4765 grp_list[0] = id; 4766 grp_list[1] = 0; 4767 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list, 4768 CLUSTER_ADD); 4769 } 4770 } 4771 } 4772 4773 /* 4774 * Handle ":syntax include [@{group-name}] filename" command. 4775 */ 4776 static void 4777 syn_cmd_include(exarg_T *eap, int syncing UNUSED) 4778 { 4779 char_u *arg = eap->arg; 4780 int sgl_id = 1; 4781 char_u *group_name_end; 4782 char_u *rest; 4783 char *errormsg = NULL; 4784 int prev_toplvl_grp; 4785 int prev_syn_inc_tag; 4786 int source = FALSE; 4787 4788 eap->nextcmd = find_nextcmd(arg); 4789 if (eap->skip) 4790 return; 4791 4792 if (arg[0] == '@') 4793 { 4794 ++arg; 4795 rest = get_group_name(arg, &group_name_end); 4796 if (rest == NULL) 4797 { 4798 emsg(_("E397: Filename required")); 4799 return; 4800 } 4801 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg)); 4802 if (sgl_id == 0) 4803 return; 4804 /* separate_nextcmd() and expand_filename() depend on this */ 4805 eap->arg = rest; 4806 } 4807 4808 /* 4809 * Everything that's left, up to the next command, should be the 4810 * filename to include. 4811 */ 4812 eap->argt |= (XFILE | NOSPC); 4813 separate_nextcmd(eap); 4814 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg)) 4815 { 4816 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the 4817 * file. Need to expand the file name first. In other cases 4818 * ":runtime!" is used. */ 4819 source = TRUE; 4820 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL) 4821 { 4822 if (errormsg != NULL) 4823 emsg(errormsg); 4824 return; 4825 } 4826 } 4827 4828 /* 4829 * Save and restore the existing top-level grouplist id and ":syn 4830 * include" tag around the actual inclusion. 4831 */ 4832 if (running_syn_inc_tag >= MAX_SYN_INC_TAG) 4833 { 4834 emsg(_("E847: Too many syntax includes")); 4835 return; 4836 } 4837 prev_syn_inc_tag = current_syn_inc_tag; 4838 current_syn_inc_tag = ++running_syn_inc_tag; 4839 prev_toplvl_grp = curwin->w_s->b_syn_topgrp; 4840 curwin->w_s->b_syn_topgrp = sgl_id; 4841 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL 4842 : source_runtime(eap->arg, DIP_ALL) == FAIL) 4843 semsg(_(e_notopen), eap->arg); 4844 curwin->w_s->b_syn_topgrp = prev_toplvl_grp; 4845 current_syn_inc_tag = prev_syn_inc_tag; 4846 } 4847 4848 /* 4849 * Handle ":syntax keyword {group-name} [{option}] keyword .." command. 4850 */ 4851 static void 4852 syn_cmd_keyword(exarg_T *eap, int syncing UNUSED) 4853 { 4854 char_u *arg = eap->arg; 4855 char_u *group_name_end; 4856 int syn_id; 4857 char_u *rest; 4858 char_u *keyword_copy = NULL; 4859 char_u *p; 4860 char_u *kw; 4861 syn_opt_arg_T syn_opt_arg; 4862 int cnt; 4863 int conceal_char = NUL; 4864 4865 rest = get_group_name(arg, &group_name_end); 4866 4867 if (rest != NULL) 4868 { 4869 if (eap->skip) 4870 syn_id = -1; 4871 else 4872 syn_id = syn_check_group(arg, (int)(group_name_end - arg)); 4873 if (syn_id != 0) 4874 /* allocate a buffer, for removing backslashes in the keyword */ 4875 keyword_copy = alloc((unsigned)STRLEN(rest) + 1); 4876 if (keyword_copy != NULL) 4877 { 4878 syn_opt_arg.flags = 0; 4879 syn_opt_arg.keyword = TRUE; 4880 syn_opt_arg.sync_idx = NULL; 4881 syn_opt_arg.has_cont_list = FALSE; 4882 syn_opt_arg.cont_in_list = NULL; 4883 syn_opt_arg.next_list = NULL; 4884 4885 /* 4886 * The options given apply to ALL keywords, so all options must be 4887 * found before keywords can be created. 4888 * 1: collect the options and copy the keywords to keyword_copy. 4889 */ 4890 cnt = 0; 4891 p = keyword_copy; 4892 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest)) 4893 { 4894 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, 4895 eap->skip); 4896 if (rest == NULL || ends_excmd(*rest)) 4897 break; 4898 /* Copy the keyword, removing backslashes, and add a NUL. */ 4899 while (*rest != NUL && !VIM_ISWHITE(*rest)) 4900 { 4901 if (*rest == '\\' && rest[1] != NUL) 4902 ++rest; 4903 *p++ = *rest++; 4904 } 4905 *p++ = NUL; 4906 ++cnt; 4907 } 4908 4909 if (!eap->skip) 4910 { 4911 /* Adjust flags for use of ":syn include". */ 4912 syn_incl_toplevel(syn_id, &syn_opt_arg.flags); 4913 4914 /* 4915 * 2: Add an entry for each keyword. 4916 */ 4917 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1) 4918 { 4919 for (p = vim_strchr(kw, '['); ; ) 4920 { 4921 if (p != NULL) 4922 *p = NUL; 4923 add_keyword(kw, syn_id, syn_opt_arg.flags, 4924 syn_opt_arg.cont_in_list, 4925 syn_opt_arg.next_list, conceal_char); 4926 if (p == NULL) 4927 break; 4928 if (p[1] == NUL) 4929 { 4930 semsg(_("E789: Missing ']': %s"), kw); 4931 goto error; 4932 } 4933 if (p[1] == ']') 4934 { 4935 if (p[2] != NUL) 4936 { 4937 semsg(_("E890: trailing char after ']': %s]%s"), 4938 kw, &p[2]); 4939 goto error; 4940 } 4941 kw = p + 1; /* skip over the "]" */ 4942 break; 4943 } 4944 if (has_mbyte) 4945 { 4946 int l = (*mb_ptr2len)(p + 1); 4947 4948 mch_memmove(p, p + 1, l); 4949 p += l; 4950 } 4951 else 4952 { 4953 p[0] = p[1]; 4954 ++p; 4955 } 4956 } 4957 } 4958 } 4959 error: 4960 vim_free(keyword_copy); 4961 vim_free(syn_opt_arg.cont_in_list); 4962 vim_free(syn_opt_arg.next_list); 4963 } 4964 } 4965 4966 if (rest != NULL) 4967 eap->nextcmd = check_nextcmd(rest); 4968 else 4969 semsg(_(e_invarg2), arg); 4970 4971 redraw_curbuf_later(SOME_VALID); 4972 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ 4973 } 4974 4975 /* 4976 * Handle ":syntax match {name} [{options}] {pattern} [{options}]". 4977 * 4978 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .." 4979 */ 4980 static void 4981 syn_cmd_match( 4982 exarg_T *eap, 4983 int syncing) /* TRUE for ":syntax sync match .. " */ 4984 { 4985 char_u *arg = eap->arg; 4986 char_u *group_name_end; 4987 char_u *rest; 4988 synpat_T item; /* the item found in the line */ 4989 int syn_id; 4990 int idx; 4991 syn_opt_arg_T syn_opt_arg; 4992 int sync_idx = 0; 4993 int conceal_char = NUL; 4994 4995 /* Isolate the group name, check for validity */ 4996 rest = get_group_name(arg, &group_name_end); 4997 4998 /* Get options before the pattern */ 4999 syn_opt_arg.flags = 0; 5000 syn_opt_arg.keyword = FALSE; 5001 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL; 5002 syn_opt_arg.has_cont_list = TRUE; 5003 syn_opt_arg.cont_list = NULL; 5004 syn_opt_arg.cont_in_list = NULL; 5005 syn_opt_arg.next_list = NULL; 5006 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip); 5007 5008 /* get the pattern. */ 5009 init_syn_patterns(); 5010 vim_memset(&item, 0, sizeof(item)); 5011 rest = get_syn_pattern(rest, &item); 5012 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL)) 5013 syn_opt_arg.flags |= HL_HAS_EOL; 5014 5015 /* Get options after the pattern */ 5016 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip); 5017 5018 if (rest != NULL) /* all arguments are valid */ 5019 { 5020 /* 5021 * Check for trailing command and illegal trailing arguments. 5022 */ 5023 eap->nextcmd = check_nextcmd(rest); 5024 if (!ends_excmd(*rest) || eap->skip) 5025 rest = NULL; 5026 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL 5027 && (syn_id = syn_check_group(arg, 5028 (int)(group_name_end - arg))) != 0) 5029 { 5030 syn_incl_toplevel(syn_id, &syn_opt_arg.flags); 5031 /* 5032 * Store the pattern in the syn_items list 5033 */ 5034 idx = curwin->w_s->b_syn_patterns.ga_len; 5035 SYN_ITEMS(curwin->w_s)[idx] = item; 5036 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing; 5037 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH; 5038 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id; 5039 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag; 5040 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags; 5041 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx; 5042 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list; 5043 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list = 5044 syn_opt_arg.cont_in_list; 5045 #ifdef FEAT_CONCEAL 5046 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char; 5047 #endif 5048 if (syn_opt_arg.cont_in_list != NULL) 5049 curwin->w_s->b_syn_containedin = TRUE; 5050 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list; 5051 ++curwin->w_s->b_syn_patterns.ga_len; 5052 5053 /* remember that we found a match for syncing on */ 5054 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE)) 5055 curwin->w_s->b_syn_sync_flags |= SF_MATCH; 5056 #ifdef FEAT_FOLDING 5057 if (syn_opt_arg.flags & HL_FOLD) 5058 ++curwin->w_s->b_syn_folditems; 5059 #endif 5060 5061 redraw_curbuf_later(SOME_VALID); 5062 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ 5063 return; /* don't free the progs and patterns now */ 5064 } 5065 } 5066 5067 /* 5068 * Something failed, free the allocated memory. 5069 */ 5070 vim_regfree(item.sp_prog); 5071 vim_free(item.sp_pattern); 5072 vim_free(syn_opt_arg.cont_list); 5073 vim_free(syn_opt_arg.cont_in_list); 5074 vim_free(syn_opt_arg.next_list); 5075 5076 if (rest == NULL) 5077 semsg(_(e_invarg2), arg); 5078 } 5079 5080 /* 5081 * Handle ":syntax region {group-name} [matchgroup={group-name}] 5082 * start {start} .. [skip {skip}] end {end} .. [{options}]". 5083 */ 5084 static void 5085 syn_cmd_region( 5086 exarg_T *eap, 5087 int syncing) /* TRUE for ":syntax sync region .." */ 5088 { 5089 char_u *arg = eap->arg; 5090 char_u *group_name_end; 5091 char_u *rest; /* next arg, NULL on error */ 5092 char_u *key_end; 5093 char_u *key = NULL; 5094 char_u *p; 5095 int item; 5096 #define ITEM_START 0 5097 #define ITEM_SKIP 1 5098 #define ITEM_END 2 5099 #define ITEM_MATCHGROUP 3 5100 struct pat_ptr 5101 { 5102 synpat_T *pp_synp; /* pointer to syn_pattern */ 5103 int pp_matchgroup_id; /* matchgroup ID */ 5104 struct pat_ptr *pp_next; /* pointer to next pat_ptr */ 5105 } *(pat_ptrs[3]); 5106 /* patterns found in the line */ 5107 struct pat_ptr *ppp; 5108 struct pat_ptr *ppp_next; 5109 int pat_count = 0; /* nr of syn_patterns found */ 5110 int syn_id; 5111 int matchgroup_id = 0; 5112 int not_enough = FALSE; /* not enough arguments */ 5113 int illegal = FALSE; /* illegal arguments */ 5114 int success = FALSE; 5115 int idx; 5116 syn_opt_arg_T syn_opt_arg; 5117 int conceal_char = NUL; 5118 5119 /* Isolate the group name, check for validity */ 5120 rest = get_group_name(arg, &group_name_end); 5121 5122 pat_ptrs[0] = NULL; 5123 pat_ptrs[1] = NULL; 5124 pat_ptrs[2] = NULL; 5125 5126 init_syn_patterns(); 5127 5128 syn_opt_arg.flags = 0; 5129 syn_opt_arg.keyword = FALSE; 5130 syn_opt_arg.sync_idx = NULL; 5131 syn_opt_arg.has_cont_list = TRUE; 5132 syn_opt_arg.cont_list = NULL; 5133 syn_opt_arg.cont_in_list = NULL; 5134 syn_opt_arg.next_list = NULL; 5135 5136 /* 5137 * get the options, patterns and matchgroup. 5138 */ 5139 while (rest != NULL && !ends_excmd(*rest)) 5140 { 5141 /* Check for option arguments */ 5142 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip); 5143 if (rest == NULL || ends_excmd(*rest)) 5144 break; 5145 5146 /* must be a pattern or matchgroup then */ 5147 key_end = rest; 5148 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=') 5149 ++key_end; 5150 vim_free(key); 5151 key = vim_strnsave_up(rest, (int)(key_end - rest)); 5152 if (key == NULL) /* out of memory */ 5153 { 5154 rest = NULL; 5155 break; 5156 } 5157 if (STRCMP(key, "MATCHGROUP") == 0) 5158 item = ITEM_MATCHGROUP; 5159 else if (STRCMP(key, "START") == 0) 5160 item = ITEM_START; 5161 else if (STRCMP(key, "END") == 0) 5162 item = ITEM_END; 5163 else if (STRCMP(key, "SKIP") == 0) 5164 { 5165 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */ 5166 { 5167 illegal = TRUE; 5168 break; 5169 } 5170 item = ITEM_SKIP; 5171 } 5172 else 5173 break; 5174 rest = skipwhite(key_end); 5175 if (*rest != '=') 5176 { 5177 rest = NULL; 5178 semsg(_("E398: Missing '=': %s"), arg); 5179 break; 5180 } 5181 rest = skipwhite(rest + 1); 5182 if (*rest == NUL) 5183 { 5184 not_enough = TRUE; 5185 break; 5186 } 5187 5188 if (item == ITEM_MATCHGROUP) 5189 { 5190 p = skiptowhite(rest); 5191 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip) 5192 matchgroup_id = 0; 5193 else 5194 { 5195 matchgroup_id = syn_check_group(rest, (int)(p - rest)); 5196 if (matchgroup_id == 0) 5197 { 5198 illegal = TRUE; 5199 break; 5200 } 5201 } 5202 rest = skipwhite(p); 5203 } 5204 else 5205 { 5206 /* 5207 * Allocate room for a syn_pattern, and link it in the list of 5208 * syn_patterns for this item, at the start (because the list is 5209 * used from end to start). 5210 */ 5211 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr)); 5212 if (ppp == NULL) 5213 { 5214 rest = NULL; 5215 break; 5216 } 5217 ppp->pp_next = pat_ptrs[item]; 5218 pat_ptrs[item] = ppp; 5219 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T)); 5220 if (ppp->pp_synp == NULL) 5221 { 5222 rest = NULL; 5223 break; 5224 } 5225 5226 /* 5227 * Get the syntax pattern and the following offset(s). 5228 */ 5229 /* Enable the appropriate \z specials. */ 5230 if (item == ITEM_START) 5231 reg_do_extmatch = REX_SET; 5232 else if (item == ITEM_SKIP || item == ITEM_END) 5233 reg_do_extmatch = REX_USE; 5234 rest = get_syn_pattern(rest, ppp->pp_synp); 5235 reg_do_extmatch = 0; 5236 if (item == ITEM_END && vim_regcomp_had_eol() 5237 && !(syn_opt_arg.flags & HL_EXCLUDENL)) 5238 ppp->pp_synp->sp_flags |= HL_HAS_EOL; 5239 ppp->pp_matchgroup_id = matchgroup_id; 5240 ++pat_count; 5241 } 5242 } 5243 vim_free(key); 5244 if (illegal || not_enough) 5245 rest = NULL; 5246 5247 /* 5248 * Must have a "start" and "end" pattern. 5249 */ 5250 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL || 5251 pat_ptrs[ITEM_END] == NULL)) 5252 { 5253 not_enough = TRUE; 5254 rest = NULL; 5255 } 5256 5257 if (rest != NULL) 5258 { 5259 /* 5260 * Check for trailing garbage or command. 5261 * If OK, add the item. 5262 */ 5263 eap->nextcmd = check_nextcmd(rest); 5264 if (!ends_excmd(*rest) || eap->skip) 5265 rest = NULL; 5266 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL 5267 && (syn_id = syn_check_group(arg, 5268 (int)(group_name_end - arg))) != 0) 5269 { 5270 syn_incl_toplevel(syn_id, &syn_opt_arg.flags); 5271 /* 5272 * Store the start/skip/end in the syn_items list 5273 */ 5274 idx = curwin->w_s->b_syn_patterns.ga_len; 5275 for (item = ITEM_START; item <= ITEM_END; ++item) 5276 { 5277 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next) 5278 { 5279 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp); 5280 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing; 5281 SYN_ITEMS(curwin->w_s)[idx].sp_type = 5282 (item == ITEM_START) ? SPTYPE_START : 5283 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END; 5284 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags; 5285 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id; 5286 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = 5287 current_syn_inc_tag; 5288 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id = 5289 ppp->pp_matchgroup_id; 5290 #ifdef FEAT_CONCEAL 5291 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char; 5292 #endif 5293 if (item == ITEM_START) 5294 { 5295 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = 5296 syn_opt_arg.cont_list; 5297 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list = 5298 syn_opt_arg.cont_in_list; 5299 if (syn_opt_arg.cont_in_list != NULL) 5300 curwin->w_s->b_syn_containedin = TRUE; 5301 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = 5302 syn_opt_arg.next_list; 5303 } 5304 ++curwin->w_s->b_syn_patterns.ga_len; 5305 ++idx; 5306 #ifdef FEAT_FOLDING 5307 if (syn_opt_arg.flags & HL_FOLD) 5308 ++curwin->w_s->b_syn_folditems; 5309 #endif 5310 } 5311 } 5312 5313 redraw_curbuf_later(SOME_VALID); 5314 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ 5315 success = TRUE; /* don't free the progs and patterns now */ 5316 } 5317 } 5318 5319 /* 5320 * Free the allocated memory. 5321 */ 5322 for (item = ITEM_START; item <= ITEM_END; ++item) 5323 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next) 5324 { 5325 if (!success) 5326 { 5327 vim_regfree(ppp->pp_synp->sp_prog); 5328 vim_free(ppp->pp_synp->sp_pattern); 5329 } 5330 vim_free(ppp->pp_synp); 5331 ppp_next = ppp->pp_next; 5332 vim_free(ppp); 5333 } 5334 5335 if (!success) 5336 { 5337 vim_free(syn_opt_arg.cont_list); 5338 vim_free(syn_opt_arg.cont_in_list); 5339 vim_free(syn_opt_arg.next_list); 5340 if (not_enough) 5341 semsg(_("E399: Not enough arguments: syntax region %s"), arg); 5342 else if (illegal || rest == NULL) 5343 semsg(_(e_invarg2), arg); 5344 } 5345 } 5346 5347 /* 5348 * A simple syntax group ID comparison function suitable for use in qsort() 5349 */ 5350 static int 5351 syn_compare_stub(const void *v1, const void *v2) 5352 { 5353 const short *s1 = v1; 5354 const short *s2 = v2; 5355 5356 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0); 5357 } 5358 5359 /* 5360 * Combines lists of syntax clusters. 5361 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed. 5362 */ 5363 static void 5364 syn_combine_list(short **clstr1, short **clstr2, int list_op) 5365 { 5366 int count1 = 0; 5367 int count2 = 0; 5368 short *g1; 5369 short *g2; 5370 short *clstr = NULL; 5371 int count; 5372 int round; 5373 5374 /* 5375 * Handle degenerate cases. 5376 */ 5377 if (*clstr2 == NULL) 5378 return; 5379 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE) 5380 { 5381 if (list_op == CLUSTER_REPLACE) 5382 vim_free(*clstr1); 5383 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD) 5384 *clstr1 = *clstr2; 5385 else 5386 vim_free(*clstr2); 5387 return; 5388 } 5389 5390 for (g1 = *clstr1; *g1; g1++) 5391 ++count1; 5392 for (g2 = *clstr2; *g2; g2++) 5393 ++count2; 5394 5395 /* 5396 * For speed purposes, sort both lists. 5397 */ 5398 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub); 5399 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub); 5400 5401 /* 5402 * We proceed in two passes; in round 1, we count the elements to place 5403 * in the new list, and in round 2, we allocate and populate the new 5404 * list. For speed, we use a mergesort-like method, adding the smaller 5405 * of the current elements in each list to the new list. 5406 */ 5407 for (round = 1; round <= 2; round++) 5408 { 5409 g1 = *clstr1; 5410 g2 = *clstr2; 5411 count = 0; 5412 5413 /* 5414 * First, loop through the lists until one of them is empty. 5415 */ 5416 while (*g1 && *g2) 5417 { 5418 /* 5419 * We always want to add from the first list. 5420 */ 5421 if (*g1 < *g2) 5422 { 5423 if (round == 2) 5424 clstr[count] = *g1; 5425 count++; 5426 g1++; 5427 continue; 5428 } 5429 /* 5430 * We only want to add from the second list if we're adding the 5431 * lists. 5432 */ 5433 if (list_op == CLUSTER_ADD) 5434 { 5435 if (round == 2) 5436 clstr[count] = *g2; 5437 count++; 5438 } 5439 if (*g1 == *g2) 5440 g1++; 5441 g2++; 5442 } 5443 5444 /* 5445 * Now add the leftovers from whichever list didn't get finished 5446 * first. As before, we only want to add from the second list if 5447 * we're adding the lists. 5448 */ 5449 for (; *g1; g1++, count++) 5450 if (round == 2) 5451 clstr[count] = *g1; 5452 if (list_op == CLUSTER_ADD) 5453 for (; *g2; g2++, count++) 5454 if (round == 2) 5455 clstr[count] = *g2; 5456 5457 if (round == 1) 5458 { 5459 /* 5460 * If the group ended up empty, we don't need to allocate any 5461 * space for it. 5462 */ 5463 if (count == 0) 5464 { 5465 clstr = NULL; 5466 break; 5467 } 5468 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short))); 5469 if (clstr == NULL) 5470 break; 5471 clstr[count] = 0; 5472 } 5473 } 5474 5475 /* 5476 * Finally, put the new list in place. 5477 */ 5478 vim_free(*clstr1); 5479 vim_free(*clstr2); 5480 *clstr1 = clstr; 5481 } 5482 5483 /* 5484 * Lookup a syntax cluster name and return its ID. 5485 * If it is not found, 0 is returned. 5486 */ 5487 static int 5488 syn_scl_name2id(char_u *name) 5489 { 5490 int i; 5491 char_u *name_u; 5492 5493 /* Avoid using stricmp() too much, it's slow on some systems */ 5494 name_u = vim_strsave_up(name); 5495 if (name_u == NULL) 5496 return 0; 5497 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; ) 5498 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL 5499 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0) 5500 break; 5501 vim_free(name_u); 5502 return (i < 0 ? 0 : i + SYNID_CLUSTER); 5503 } 5504 5505 /* 5506 * Like syn_scl_name2id(), but take a pointer + length argument. 5507 */ 5508 static int 5509 syn_scl_namen2id(char_u *linep, int len) 5510 { 5511 char_u *name; 5512 int id = 0; 5513 5514 name = vim_strnsave(linep, len); 5515 if (name != NULL) 5516 { 5517 id = syn_scl_name2id(name); 5518 vim_free(name); 5519 } 5520 return id; 5521 } 5522 5523 /* 5524 * Find syntax cluster name in the table and return its ID. 5525 * The argument is a pointer to the name and the length of the name. 5526 * If it doesn't exist yet, a new entry is created. 5527 * Return 0 for failure. 5528 */ 5529 static int 5530 syn_check_cluster(char_u *pp, int len) 5531 { 5532 int id; 5533 char_u *name; 5534 5535 name = vim_strnsave(pp, len); 5536 if (name == NULL) 5537 return 0; 5538 5539 id = syn_scl_name2id(name); 5540 if (id == 0) /* doesn't exist yet */ 5541 id = syn_add_cluster(name); 5542 else 5543 vim_free(name); 5544 return id; 5545 } 5546 5547 /* 5548 * Add new syntax cluster and return its ID. 5549 * "name" must be an allocated string, it will be consumed. 5550 * Return 0 for failure. 5551 */ 5552 static int 5553 syn_add_cluster(char_u *name) 5554 { 5555 int len; 5556 5557 /* 5558 * First call for this growarray: init growing array. 5559 */ 5560 if (curwin->w_s->b_syn_clusters.ga_data == NULL) 5561 { 5562 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T); 5563 curwin->w_s->b_syn_clusters.ga_growsize = 10; 5564 } 5565 5566 len = curwin->w_s->b_syn_clusters.ga_len; 5567 if (len >= MAX_CLUSTER_ID) 5568 { 5569 emsg(_("E848: Too many syntax clusters")); 5570 vim_free(name); 5571 return 0; 5572 } 5573 5574 /* 5575 * Make room for at least one other cluster entry. 5576 */ 5577 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL) 5578 { 5579 vim_free(name); 5580 return 0; 5581 } 5582 5583 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T)); 5584 SYN_CLSTR(curwin->w_s)[len].scl_name = name; 5585 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name); 5586 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL; 5587 ++curwin->w_s->b_syn_clusters.ga_len; 5588 5589 if (STRICMP(name, "Spell") == 0) 5590 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER; 5591 if (STRICMP(name, "NoSpell") == 0) 5592 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER; 5593 5594 return len + SYNID_CLUSTER; 5595 } 5596 5597 /* 5598 * Handle ":syntax cluster {cluster-name} [contains={groupname},..] 5599 * [add={groupname},..] [remove={groupname},..]". 5600 */ 5601 static void 5602 syn_cmd_cluster(exarg_T *eap, int syncing UNUSED) 5603 { 5604 char_u *arg = eap->arg; 5605 char_u *group_name_end; 5606 char_u *rest; 5607 int scl_id; 5608 short *clstr_list; 5609 int got_clstr = FALSE; 5610 int opt_len; 5611 int list_op; 5612 5613 eap->nextcmd = find_nextcmd(arg); 5614 if (eap->skip) 5615 return; 5616 5617 rest = get_group_name(arg, &group_name_end); 5618 5619 if (rest != NULL) 5620 { 5621 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg)); 5622 if (scl_id == 0) 5623 return; 5624 scl_id -= SYNID_CLUSTER; 5625 5626 for (;;) 5627 { 5628 if (STRNICMP(rest, "add", 3) == 0 5629 && (VIM_ISWHITE(rest[3]) || rest[3] == '=')) 5630 { 5631 opt_len = 3; 5632 list_op = CLUSTER_ADD; 5633 } 5634 else if (STRNICMP(rest, "remove", 6) == 0 5635 && (VIM_ISWHITE(rest[6]) || rest[6] == '=')) 5636 { 5637 opt_len = 6; 5638 list_op = CLUSTER_SUBTRACT; 5639 } 5640 else if (STRNICMP(rest, "contains", 8) == 0 5641 && (VIM_ISWHITE(rest[8]) || rest[8] == '=')) 5642 { 5643 opt_len = 8; 5644 list_op = CLUSTER_REPLACE; 5645 } 5646 else 5647 break; 5648 5649 clstr_list = NULL; 5650 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL) 5651 { 5652 semsg(_(e_invarg2), rest); 5653 break; 5654 } 5655 if (scl_id >= 0) 5656 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list, 5657 &clstr_list, list_op); 5658 else 5659 vim_free(clstr_list); 5660 got_clstr = TRUE; 5661 } 5662 5663 if (got_clstr) 5664 { 5665 redraw_curbuf_later(SOME_VALID); 5666 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */ 5667 } 5668 } 5669 5670 if (!got_clstr) 5671 emsg(_("E400: No cluster specified")); 5672 if (rest == NULL || !ends_excmd(*rest)) 5673 semsg(_(e_invarg2), arg); 5674 } 5675 5676 /* 5677 * On first call for current buffer: Init growing array. 5678 */ 5679 static void 5680 init_syn_patterns(void) 5681 { 5682 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T); 5683 curwin->w_s->b_syn_patterns.ga_growsize = 10; 5684 } 5685 5686 /* 5687 * Get one pattern for a ":syntax match" or ":syntax region" command. 5688 * Stores the pattern and program in a synpat_T. 5689 * Returns a pointer to the next argument, or NULL in case of an error. 5690 */ 5691 static char_u * 5692 get_syn_pattern(char_u *arg, synpat_T *ci) 5693 { 5694 char_u *end; 5695 int *p; 5696 int idx; 5697 char_u *cpo_save; 5698 5699 /* need at least three chars */ 5700 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL) 5701 return NULL; 5702 5703 end = skip_regexp(arg + 1, *arg, TRUE, NULL); 5704 if (*end != *arg) /* end delimiter not found */ 5705 { 5706 semsg(_("E401: Pattern delimiter not found: %s"), arg); 5707 return NULL; 5708 } 5709 /* store the pattern and compiled regexp program */ 5710 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL) 5711 return NULL; 5712 5713 /* Make 'cpoptions' empty, to avoid the 'l' flag */ 5714 cpo_save = p_cpo; 5715 p_cpo = (char_u *)""; 5716 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC); 5717 p_cpo = cpo_save; 5718 5719 if (ci->sp_prog == NULL) 5720 return NULL; 5721 ci->sp_ic = curwin->w_s->b_syn_ic; 5722 #ifdef FEAT_PROFILE 5723 syn_clear_time(&ci->sp_time); 5724 #endif 5725 5726 /* 5727 * Check for a match, highlight or region offset. 5728 */ 5729 ++end; 5730 do 5731 { 5732 for (idx = SPO_COUNT; --idx >= 0; ) 5733 if (STRNCMP(end, spo_name_tab[idx], 3) == 0) 5734 break; 5735 if (idx >= 0) 5736 { 5737 p = &(ci->sp_offsets[idx]); 5738 if (idx != SPO_LC_OFF) 5739 switch (end[3]) 5740 { 5741 case 's': break; 5742 case 'b': break; 5743 case 'e': idx += SPO_COUNT; break; 5744 default: idx = -1; break; 5745 } 5746 if (idx >= 0) 5747 { 5748 ci->sp_off_flags |= (1 << idx); 5749 if (idx == SPO_LC_OFF) /* lc=99 */ 5750 { 5751 end += 3; 5752 *p = getdigits(&end); 5753 5754 /* "lc=" offset automatically sets "ms=" offset */ 5755 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF))) 5756 { 5757 ci->sp_off_flags |= (1 << SPO_MS_OFF); 5758 ci->sp_offsets[SPO_MS_OFF] = *p; 5759 } 5760 } 5761 else /* yy=x+99 */ 5762 { 5763 end += 4; 5764 if (*end == '+') 5765 { 5766 ++end; 5767 *p = getdigits(&end); /* positive offset */ 5768 } 5769 else if (*end == '-') 5770 { 5771 ++end; 5772 *p = -getdigits(&end); /* negative offset */ 5773 } 5774 } 5775 if (*end != ',') 5776 break; 5777 ++end; 5778 } 5779 } 5780 } while (idx >= 0); 5781 5782 if (!ends_excmd(*end) && !VIM_ISWHITE(*end)) 5783 { 5784 semsg(_("E402: Garbage after pattern: %s"), arg); 5785 return NULL; 5786 } 5787 return skipwhite(end); 5788 } 5789 5790 /* 5791 * Handle ":syntax sync .." command. 5792 */ 5793 static void 5794 syn_cmd_sync(exarg_T *eap, int syncing UNUSED) 5795 { 5796 char_u *arg_start = eap->arg; 5797 char_u *arg_end; 5798 char_u *key = NULL; 5799 char_u *next_arg; 5800 int illegal = FALSE; 5801 int finished = FALSE; 5802 long n; 5803 char_u *cpo_save; 5804 5805 if (ends_excmd(*arg_start)) 5806 { 5807 syn_cmd_list(eap, TRUE); 5808 return; 5809 } 5810 5811 while (!ends_excmd(*arg_start)) 5812 { 5813 arg_end = skiptowhite(arg_start); 5814 next_arg = skipwhite(arg_end); 5815 vim_free(key); 5816 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start)); 5817 if (STRCMP(key, "CCOMMENT") == 0) 5818 { 5819 if (!eap->skip) 5820 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT; 5821 if (!ends_excmd(*next_arg)) 5822 { 5823 arg_end = skiptowhite(next_arg); 5824 if (!eap->skip) 5825 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg, 5826 (int)(arg_end - next_arg)); 5827 next_arg = skipwhite(arg_end); 5828 } 5829 else if (!eap->skip) 5830 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment"); 5831 } 5832 else if ( STRNCMP(key, "LINES", 5) == 0 5833 || STRNCMP(key, "MINLINES", 8) == 0 5834 || STRNCMP(key, "MAXLINES", 8) == 0 5835 || STRNCMP(key, "LINEBREAKS", 10) == 0) 5836 { 5837 if (key[4] == 'S') 5838 arg_end = key + 6; 5839 else if (key[0] == 'L') 5840 arg_end = key + 11; 5841 else 5842 arg_end = key + 9; 5843 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end)) 5844 { 5845 illegal = TRUE; 5846 break; 5847 } 5848 n = getdigits(&arg_end); 5849 if (!eap->skip) 5850 { 5851 if (key[4] == 'B') 5852 curwin->w_s->b_syn_sync_linebreaks = n; 5853 else if (key[1] == 'A') 5854 curwin->w_s->b_syn_sync_maxlines = n; 5855 else 5856 curwin->w_s->b_syn_sync_minlines = n; 5857 } 5858 } 5859 else if (STRCMP(key, "FROMSTART") == 0) 5860 { 5861 if (!eap->skip) 5862 { 5863 curwin->w_s->b_syn_sync_minlines = MAXLNUM; 5864 curwin->w_s->b_syn_sync_maxlines = 0; 5865 } 5866 } 5867 else if (STRCMP(key, "LINECONT") == 0) 5868 { 5869 if (*next_arg == NUL) /* missing pattern */ 5870 { 5871 illegal = TRUE; 5872 break; 5873 } 5874 if (curwin->w_s->b_syn_linecont_pat != NULL) 5875 { 5876 emsg(_("E403: syntax sync: line continuations pattern specified twice")); 5877 finished = TRUE; 5878 break; 5879 } 5880 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL); 5881 if (*arg_end != *next_arg) /* end delimiter not found */ 5882 { 5883 illegal = TRUE; 5884 break; 5885 } 5886 5887 if (!eap->skip) 5888 { 5889 /* store the pattern and compiled regexp program */ 5890 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1, 5891 (int)(arg_end - next_arg - 1))) == NULL) 5892 { 5893 finished = TRUE; 5894 break; 5895 } 5896 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic; 5897 5898 /* Make 'cpoptions' empty, to avoid the 'l' flag */ 5899 cpo_save = p_cpo; 5900 p_cpo = (char_u *)""; 5901 curwin->w_s->b_syn_linecont_prog = 5902 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC); 5903 p_cpo = cpo_save; 5904 #ifdef FEAT_PROFILE 5905 syn_clear_time(&curwin->w_s->b_syn_linecont_time); 5906 #endif 5907 5908 if (curwin->w_s->b_syn_linecont_prog == NULL) 5909 { 5910 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat); 5911 finished = TRUE; 5912 break; 5913 } 5914 } 5915 next_arg = skipwhite(arg_end + 1); 5916 } 5917 else 5918 { 5919 eap->arg = next_arg; 5920 if (STRCMP(key, "MATCH") == 0) 5921 syn_cmd_match(eap, TRUE); 5922 else if (STRCMP(key, "REGION") == 0) 5923 syn_cmd_region(eap, TRUE); 5924 else if (STRCMP(key, "CLEAR") == 0) 5925 syn_cmd_clear(eap, TRUE); 5926 else 5927 illegal = TRUE; 5928 finished = TRUE; 5929 break; 5930 } 5931 arg_start = next_arg; 5932 } 5933 vim_free(key); 5934 if (illegal) 5935 semsg(_("E404: Illegal arguments: %s"), arg_start); 5936 else if (!finished) 5937 { 5938 eap->nextcmd = check_nextcmd(arg_start); 5939 redraw_curbuf_later(SOME_VALID); 5940 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ 5941 } 5942 } 5943 5944 /* 5945 * Convert a line of highlight group names into a list of group ID numbers. 5946 * "arg" should point to the "contains" or "nextgroup" keyword. 5947 * "arg" is advanced to after the last group name. 5948 * Careful: the argument is modified (NULs added). 5949 * returns FAIL for some error, OK for success. 5950 */ 5951 static int 5952 get_id_list( 5953 char_u **arg, 5954 int keylen, /* length of keyword */ 5955 short **list, /* where to store the resulting list, if not 5956 NULL, the list is silently skipped! */ 5957 int skip) 5958 { 5959 char_u *p = NULL; 5960 char_u *end; 5961 int round; 5962 int count; 5963 int total_count = 0; 5964 short *retval = NULL; 5965 char_u *name; 5966 regmatch_T regmatch; 5967 int id; 5968 int i; 5969 int failed = FALSE; 5970 5971 /* 5972 * We parse the list twice: 5973 * round == 1: count the number of items, allocate the array. 5974 * round == 2: fill the array with the items. 5975 * In round 1 new groups may be added, causing the number of items to 5976 * grow when a regexp is used. In that case round 1 is done once again. 5977 */ 5978 for (round = 1; round <= 2; ++round) 5979 { 5980 /* 5981 * skip "contains" 5982 */ 5983 p = skipwhite(*arg + keylen); 5984 if (*p != '=') 5985 { 5986 semsg(_("E405: Missing equal sign: %s"), *arg); 5987 break; 5988 } 5989 p = skipwhite(p + 1); 5990 if (ends_excmd(*p)) 5991 { 5992 semsg(_("E406: Empty argument: %s"), *arg); 5993 break; 5994 } 5995 5996 /* 5997 * parse the arguments after "contains" 5998 */ 5999 count = 0; 6000 while (!ends_excmd(*p)) 6001 { 6002 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end) 6003 ; 6004 name = alloc((int)(end - p + 3)); /* leave room for "^$" */ 6005 if (name == NULL) 6006 { 6007 failed = TRUE; 6008 break; 6009 } 6010 vim_strncpy(name + 1, p, end - p); 6011 if ( STRCMP(name + 1, "ALLBUT") == 0 6012 || STRCMP(name + 1, "ALL") == 0 6013 || STRCMP(name + 1, "TOP") == 0 6014 || STRCMP(name + 1, "CONTAINED") == 0) 6015 { 6016 if (TOUPPER_ASC(**arg) != 'C') 6017 { 6018 semsg(_("E407: %s not allowed here"), name + 1); 6019 failed = TRUE; 6020 vim_free(name); 6021 break; 6022 } 6023 if (count != 0) 6024 { 6025 semsg(_("E408: %s must be first in contains list"), 6026 name + 1); 6027 failed = TRUE; 6028 vim_free(name); 6029 break; 6030 } 6031 if (name[1] == 'A') 6032 id = SYNID_ALLBUT; 6033 else if (name[1] == 'T') 6034 id = SYNID_TOP; 6035 else 6036 id = SYNID_CONTAINED; 6037 id += current_syn_inc_tag; 6038 } 6039 else if (name[1] == '@') 6040 { 6041 if (skip) 6042 id = -1; 6043 else 6044 id = syn_check_cluster(name + 2, (int)(end - p - 1)); 6045 } 6046 else 6047 { 6048 /* 6049 * Handle full group name. 6050 */ 6051 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL) 6052 id = syn_check_group(name + 1, (int)(end - p)); 6053 else 6054 { 6055 /* 6056 * Handle match of regexp with group names. 6057 */ 6058 *name = '^'; 6059 STRCAT(name, "$"); 6060 regmatch.regprog = vim_regcomp(name, RE_MAGIC); 6061 if (regmatch.regprog == NULL) 6062 { 6063 failed = TRUE; 6064 vim_free(name); 6065 break; 6066 } 6067 6068 regmatch.rm_ic = TRUE; 6069 id = 0; 6070 for (i = highlight_ga.ga_len; --i >= 0; ) 6071 { 6072 if (vim_regexec(®match, HL_TABLE()[i].sg_name, 6073 (colnr_T)0)) 6074 { 6075 if (round == 2) 6076 { 6077 /* Got more items than expected; can happen 6078 * when adding items that match: 6079 * "contains=a.*b,axb". 6080 * Go back to first round */ 6081 if (count >= total_count) 6082 { 6083 vim_free(retval); 6084 round = 1; 6085 } 6086 else 6087 retval[count] = i + 1; 6088 } 6089 ++count; 6090 id = -1; /* remember that we found one */ 6091 } 6092 } 6093 vim_regfree(regmatch.regprog); 6094 } 6095 } 6096 vim_free(name); 6097 if (id == 0) 6098 { 6099 semsg(_("E409: Unknown group name: %s"), p); 6100 failed = TRUE; 6101 break; 6102 } 6103 if (id > 0) 6104 { 6105 if (round == 2) 6106 { 6107 /* Got more items than expected, go back to first round */ 6108 if (count >= total_count) 6109 { 6110 vim_free(retval); 6111 round = 1; 6112 } 6113 else 6114 retval[count] = id; 6115 } 6116 ++count; 6117 } 6118 p = skipwhite(end); 6119 if (*p != ',') 6120 break; 6121 p = skipwhite(p + 1); /* skip comma in between arguments */ 6122 } 6123 if (failed) 6124 break; 6125 if (round == 1) 6126 { 6127 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short))); 6128 if (retval == NULL) 6129 break; 6130 retval[count] = 0; /* zero means end of the list */ 6131 total_count = count; 6132 } 6133 } 6134 6135 *arg = p; 6136 if (failed || retval == NULL) 6137 { 6138 vim_free(retval); 6139 return FAIL; 6140 } 6141 6142 if (*list == NULL) 6143 *list = retval; 6144 else 6145 vim_free(retval); /* list already found, don't overwrite it */ 6146 6147 return OK; 6148 } 6149 6150 /* 6151 * Make a copy of an ID list. 6152 */ 6153 static short * 6154 copy_id_list(short *list) 6155 { 6156 int len; 6157 int count; 6158 short *retval; 6159 6160 if (list == NULL) 6161 return NULL; 6162 6163 for (count = 0; list[count]; ++count) 6164 ; 6165 len = (count + 1) * sizeof(short); 6166 retval = (short *)alloc((unsigned)len); 6167 if (retval != NULL) 6168 mch_memmove(retval, list, (size_t)len); 6169 6170 return retval; 6171 } 6172 6173 /* 6174 * Check if syntax group "ssp" is in the ID list "list" of "cur_si". 6175 * "cur_si" can be NULL if not checking the "containedin" list. 6176 * Used to check if a syntax item is in the "contains" or "nextgroup" list of 6177 * the current item. 6178 * This function is called very often, keep it fast!! 6179 */ 6180 static int 6181 in_id_list( 6182 stateitem_T *cur_si, /* current item or NULL */ 6183 short *list, /* id list */ 6184 struct sp_syn *ssp, /* group id and ":syn include" tag of group */ 6185 int contained) /* group id is contained */ 6186 { 6187 int retval; 6188 short *scl_list; 6189 short item; 6190 short id = ssp->id; 6191 static int depth = 0; 6192 int r; 6193 6194 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */ 6195 if (cur_si != NULL && ssp->cont_in_list != NULL 6196 && !(cur_si->si_flags & HL_MATCH)) 6197 { 6198 /* Ignore transparent items without a contains argument. Double check 6199 * that we don't go back past the first one. */ 6200 while ((cur_si->si_flags & HL_TRANS_CONT) 6201 && cur_si > (stateitem_T *)(current_state.ga_data)) 6202 --cur_si; 6203 /* cur_si->si_idx is -1 for keywords, these never contain anything. */ 6204 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list, 6205 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn), 6206 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED)) 6207 return TRUE; 6208 } 6209 6210 if (list == NULL) 6211 return FALSE; 6212 6213 /* 6214 * If list is ID_LIST_ALL, we are in a transparent item that isn't 6215 * inside anything. Only allow not-contained groups. 6216 */ 6217 if (list == ID_LIST_ALL) 6218 return !contained; 6219 6220 /* 6221 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the 6222 * contains list. We also require that "id" is at the same ":syn include" 6223 * level as the list. 6224 */ 6225 item = *list; 6226 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER) 6227 { 6228 if (item < SYNID_TOP) 6229 { 6230 /* ALL or ALLBUT: accept all groups in the same file */ 6231 if (item - SYNID_ALLBUT != ssp->inc_tag) 6232 return FALSE; 6233 } 6234 else if (item < SYNID_CONTAINED) 6235 { 6236 /* TOP: accept all not-contained groups in the same file */ 6237 if (item - SYNID_TOP != ssp->inc_tag || contained) 6238 return FALSE; 6239 } 6240 else 6241 { 6242 /* CONTAINED: accept all contained groups in the same file */ 6243 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained) 6244 return FALSE; 6245 } 6246 item = *++list; 6247 retval = FALSE; 6248 } 6249 else 6250 retval = TRUE; 6251 6252 /* 6253 * Return "retval" if id is in the contains list. 6254 */ 6255 while (item != 0) 6256 { 6257 if (item == id) 6258 return retval; 6259 if (item >= SYNID_CLUSTER) 6260 { 6261 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list; 6262 /* restrict recursiveness to 30 to avoid an endless loop for a 6263 * cluster that includes itself (indirectly) */ 6264 if (scl_list != NULL && depth < 30) 6265 { 6266 ++depth; 6267 r = in_id_list(NULL, scl_list, ssp, contained); 6268 --depth; 6269 if (r) 6270 return retval; 6271 } 6272 } 6273 item = *++list; 6274 } 6275 return !retval; 6276 } 6277 6278 struct subcommand 6279 { 6280 char *name; /* subcommand name */ 6281 void (*func)(exarg_T *, int); /* function to call */ 6282 }; 6283 6284 static struct subcommand subcommands[] = 6285 { 6286 {"case", syn_cmd_case}, 6287 {"clear", syn_cmd_clear}, 6288 {"cluster", syn_cmd_cluster}, 6289 {"conceal", syn_cmd_conceal}, 6290 {"enable", syn_cmd_enable}, 6291 {"include", syn_cmd_include}, 6292 {"iskeyword", syn_cmd_iskeyword}, 6293 {"keyword", syn_cmd_keyword}, 6294 {"list", syn_cmd_list}, 6295 {"manual", syn_cmd_manual}, 6296 {"match", syn_cmd_match}, 6297 {"on", syn_cmd_on}, 6298 {"off", syn_cmd_off}, 6299 {"region", syn_cmd_region}, 6300 {"reset", syn_cmd_reset}, 6301 {"spell", syn_cmd_spell}, 6302 {"sync", syn_cmd_sync}, 6303 {"", syn_cmd_list}, 6304 {NULL, NULL} 6305 }; 6306 6307 /* 6308 * ":syntax". 6309 * This searches the subcommands[] table for the subcommand name, and calls a 6310 * syntax_subcommand() function to do the rest. 6311 */ 6312 void 6313 ex_syntax(exarg_T *eap) 6314 { 6315 char_u *arg = eap->arg; 6316 char_u *subcmd_end; 6317 char_u *subcmd_name; 6318 int i; 6319 6320 syn_cmdlinep = eap->cmdlinep; 6321 6322 /* isolate subcommand name */ 6323 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end) 6324 ; 6325 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg)); 6326 if (subcmd_name != NULL) 6327 { 6328 if (eap->skip) /* skip error messages for all subcommands */ 6329 ++emsg_skip; 6330 for (i = 0; ; ++i) 6331 { 6332 if (subcommands[i].name == NULL) 6333 { 6334 semsg(_("E410: Invalid :syntax subcommand: %s"), subcmd_name); 6335 break; 6336 } 6337 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0) 6338 { 6339 eap->arg = skipwhite(subcmd_end); 6340 (subcommands[i].func)(eap, FALSE); 6341 break; 6342 } 6343 } 6344 vim_free(subcmd_name); 6345 if (eap->skip) 6346 --emsg_skip; 6347 } 6348 } 6349 6350 void 6351 ex_ownsyntax(exarg_T *eap) 6352 { 6353 char_u *old_value; 6354 char_u *new_value; 6355 6356 if (curwin->w_s == &curwin->w_buffer->b_s) 6357 { 6358 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T)); 6359 memset(curwin->w_s, 0, sizeof(synblock_T)); 6360 hash_init(&curwin->w_s->b_keywtab); 6361 hash_init(&curwin->w_s->b_keywtab_ic); 6362 #ifdef FEAT_SPELL 6363 /* TODO: keep the spell checking as it was. */ 6364 curwin->w_p_spell = FALSE; /* No spell checking */ 6365 clear_string_option(&curwin->w_s->b_p_spc); 6366 clear_string_option(&curwin->w_s->b_p_spf); 6367 clear_string_option(&curwin->w_s->b_p_spl); 6368 #endif 6369 clear_string_option(&curwin->w_s->b_syn_isk); 6370 } 6371 6372 /* save value of b:current_syntax */ 6373 old_value = get_var_value((char_u *)"b:current_syntax"); 6374 if (old_value != NULL) 6375 old_value = vim_strsave(old_value); 6376 6377 /* Apply the "syntax" autocommand event, this finds and loads the syntax 6378 * file. */ 6379 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf); 6380 6381 /* move value of b:current_syntax to w:current_syntax */ 6382 new_value = get_var_value((char_u *)"b:current_syntax"); 6383 if (new_value != NULL) 6384 set_internal_string_var((char_u *)"w:current_syntax", new_value); 6385 6386 /* restore value of b:current_syntax */ 6387 if (old_value == NULL) 6388 do_unlet((char_u *)"b:current_syntax", TRUE); 6389 else 6390 { 6391 set_internal_string_var((char_u *)"b:current_syntax", old_value); 6392 vim_free(old_value); 6393 } 6394 } 6395 6396 int 6397 syntax_present(win_T *win) 6398 { 6399 return (win->w_s->b_syn_patterns.ga_len != 0 6400 || win->w_s->b_syn_clusters.ga_len != 0 6401 || win->w_s->b_keywtab.ht_used > 0 6402 || win->w_s->b_keywtab_ic.ht_used > 0); 6403 } 6404 6405 #if defined(FEAT_CMDL_COMPL) || defined(PROTO) 6406 6407 static enum 6408 { 6409 EXP_SUBCMD, /* expand ":syn" sub-commands */ 6410 EXP_CASE, /* expand ":syn case" arguments */ 6411 EXP_SPELL, /* expand ":syn spell" arguments */ 6412 EXP_SYNC /* expand ":syn sync" arguments */ 6413 } expand_what; 6414 6415 /* 6416 * Reset include_link, include_default, include_none to 0. 6417 * Called when we are done expanding. 6418 */ 6419 void 6420 reset_expand_highlight(void) 6421 { 6422 include_link = include_default = include_none = 0; 6423 } 6424 6425 /* 6426 * Handle command line completion for :match and :echohl command: Add "None" 6427 * as highlight group. 6428 */ 6429 void 6430 set_context_in_echohl_cmd(expand_T *xp, char_u *arg) 6431 { 6432 xp->xp_context = EXPAND_HIGHLIGHT; 6433 xp->xp_pattern = arg; 6434 include_none = 1; 6435 } 6436 6437 /* 6438 * Handle command line completion for :syntax command. 6439 */ 6440 void 6441 set_context_in_syntax_cmd(expand_T *xp, char_u *arg) 6442 { 6443 char_u *p; 6444 6445 /* Default: expand subcommands */ 6446 xp->xp_context = EXPAND_SYNTAX; 6447 expand_what = EXP_SUBCMD; 6448 xp->xp_pattern = arg; 6449 include_link = 0; 6450 include_default = 0; 6451 6452 /* (part of) subcommand already typed */ 6453 if (*arg != NUL) 6454 { 6455 p = skiptowhite(arg); 6456 if (*p != NUL) /* past first word */ 6457 { 6458 xp->xp_pattern = skipwhite(p); 6459 if (*skiptowhite(xp->xp_pattern) != NUL) 6460 xp->xp_context = EXPAND_NOTHING; 6461 else if (STRNICMP(arg, "case", p - arg) == 0) 6462 expand_what = EXP_CASE; 6463 else if (STRNICMP(arg, "spell", p - arg) == 0) 6464 expand_what = EXP_SPELL; 6465 else if (STRNICMP(arg, "sync", p - arg) == 0) 6466 expand_what = EXP_SYNC; 6467 else if ( STRNICMP(arg, "keyword", p - arg) == 0 6468 || STRNICMP(arg, "region", p - arg) == 0 6469 || STRNICMP(arg, "match", p - arg) == 0 6470 || STRNICMP(arg, "list", p - arg) == 0) 6471 xp->xp_context = EXPAND_HIGHLIGHT; 6472 else 6473 xp->xp_context = EXPAND_NOTHING; 6474 } 6475 } 6476 } 6477 6478 /* 6479 * Function given to ExpandGeneric() to obtain the list syntax names for 6480 * expansion. 6481 */ 6482 char_u * 6483 get_syntax_name(expand_T *xp UNUSED, int idx) 6484 { 6485 switch (expand_what) 6486 { 6487 case EXP_SUBCMD: 6488 return (char_u *)subcommands[idx].name; 6489 case EXP_CASE: 6490 { 6491 static char *case_args[] = {"match", "ignore", NULL}; 6492 return (char_u *)case_args[idx]; 6493 } 6494 case EXP_SPELL: 6495 { 6496 static char *spell_args[] = 6497 {"toplevel", "notoplevel", "default", NULL}; 6498 return (char_u *)spell_args[idx]; 6499 } 6500 case EXP_SYNC: 6501 { 6502 static char *sync_args[] = 6503 {"ccomment", "clear", "fromstart", 6504 "linebreaks=", "linecont", "lines=", "match", 6505 "maxlines=", "minlines=", "region", NULL}; 6506 return (char_u *)sync_args[idx]; 6507 } 6508 } 6509 return NULL; 6510 } 6511 6512 #endif /* FEAT_CMDL_COMPL */ 6513 6514 /* 6515 * Function called for expression evaluation: get syntax ID at file position. 6516 */ 6517 int 6518 syn_get_id( 6519 win_T *wp, 6520 long lnum, 6521 colnr_T col, 6522 int trans, /* remove transparency */ 6523 int *spellp, /* return: can do spell checking */ 6524 int keep_state) /* keep state of char at "col" */ 6525 { 6526 /* When the position is not after the current position and in the same 6527 * line of the same buffer, need to restart parsing. */ 6528 if (wp->w_buffer != syn_buf 6529 || lnum != current_lnum 6530 || col < current_col) 6531 syntax_start(wp, lnum); 6532 else if (wp->w_buffer == syn_buf 6533 && lnum == current_lnum 6534 && col > current_col) 6535 /* next_match may not be correct when moving around, e.g. with the 6536 * "skip" expression in searchpair() */ 6537 next_match_idx = -1; 6538 6539 (void)get_syntax_attr(col, spellp, keep_state); 6540 6541 return (trans ? current_trans_id : current_id); 6542 } 6543 6544 #if defined(FEAT_CONCEAL) || defined(PROTO) 6545 /* 6546 * Get extra information about the syntax item. Must be called right after 6547 * get_syntax_attr(). 6548 * Stores the current item sequence nr in "*seqnrp". 6549 * Returns the current flags. 6550 */ 6551 int 6552 get_syntax_info(int *seqnrp) 6553 { 6554 *seqnrp = current_seqnr; 6555 return current_flags; 6556 } 6557 6558 /* 6559 * Return conceal substitution character 6560 */ 6561 int 6562 syn_get_sub_char(void) 6563 { 6564 return current_sub_char; 6565 } 6566 #endif 6567 6568 #if defined(FEAT_EVAL) || defined(PROTO) 6569 /* 6570 * Return the syntax ID at position "i" in the current stack. 6571 * The caller must have called syn_get_id() before to fill the stack. 6572 * Returns -1 when "i" is out of range. 6573 */ 6574 int 6575 syn_get_stack_item(int i) 6576 { 6577 if (i >= current_state.ga_len) 6578 { 6579 /* Need to invalidate the state, because we didn't properly finish it 6580 * for the last character, "keep_state" was TRUE. */ 6581 invalidate_current_state(); 6582 current_col = MAXCOL; 6583 return -1; 6584 } 6585 return CUR_STATE(i).si_id; 6586 } 6587 #endif 6588 6589 #if defined(FEAT_FOLDING) || defined(PROTO) 6590 /* 6591 * Function called to get folding level for line "lnum" in window "wp". 6592 */ 6593 int 6594 syn_get_foldlevel(win_T *wp, long lnum) 6595 { 6596 int level = 0; 6597 int i; 6598 6599 /* Return quickly when there are no fold items at all. */ 6600 if (wp->w_s->b_syn_folditems != 0 6601 && !wp->w_s->b_syn_error 6602 # ifdef SYN_TIME_LIMIT 6603 && !wp->w_s->b_syn_slow 6604 # endif 6605 ) 6606 { 6607 syntax_start(wp, lnum); 6608 6609 for (i = 0; i < current_state.ga_len; ++i) 6610 if (CUR_STATE(i).si_flags & HL_FOLD) 6611 ++level; 6612 } 6613 if (level > wp->w_p_fdn) 6614 { 6615 level = wp->w_p_fdn; 6616 if (level < 0) 6617 level = 0; 6618 } 6619 return level; 6620 } 6621 #endif 6622 6623 #if defined(FEAT_PROFILE) || defined(PROTO) 6624 /* 6625 * ":syntime". 6626 */ 6627 void 6628 ex_syntime(exarg_T *eap) 6629 { 6630 if (STRCMP(eap->arg, "on") == 0) 6631 syn_time_on = TRUE; 6632 else if (STRCMP(eap->arg, "off") == 0) 6633 syn_time_on = FALSE; 6634 else if (STRCMP(eap->arg, "clear") == 0) 6635 syntime_clear(); 6636 else if (STRCMP(eap->arg, "report") == 0) 6637 syntime_report(); 6638 else 6639 semsg(_(e_invarg2), eap->arg); 6640 } 6641 6642 static void 6643 syn_clear_time(syn_time_T *st) 6644 { 6645 profile_zero(&st->total); 6646 profile_zero(&st->slowest); 6647 st->count = 0; 6648 st->match = 0; 6649 } 6650 6651 /* 6652 * Clear the syntax timing for the current buffer. 6653 */ 6654 static void 6655 syntime_clear(void) 6656 { 6657 int idx; 6658 synpat_T *spp; 6659 6660 if (!syntax_present(curwin)) 6661 { 6662 msg(_(msg_no_items)); 6663 return; 6664 } 6665 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx) 6666 { 6667 spp = &(SYN_ITEMS(curwin->w_s)[idx]); 6668 syn_clear_time(&spp->sp_time); 6669 } 6670 } 6671 6672 #if defined(FEAT_CMDL_COMPL) || defined(PROTO) 6673 /* 6674 * Function given to ExpandGeneric() to obtain the possible arguments of the 6675 * ":syntime {on,off,clear,report}" command. 6676 */ 6677 char_u * 6678 get_syntime_arg(expand_T *xp UNUSED, int idx) 6679 { 6680 switch (idx) 6681 { 6682 case 0: return (char_u *)"on"; 6683 case 1: return (char_u *)"off"; 6684 case 2: return (char_u *)"clear"; 6685 case 3: return (char_u *)"report"; 6686 } 6687 return NULL; 6688 } 6689 #endif 6690 6691 typedef struct 6692 { 6693 proftime_T total; 6694 int count; 6695 int match; 6696 proftime_T slowest; 6697 proftime_T average; 6698 int id; 6699 char_u *pattern; 6700 } time_entry_T; 6701 6702 static int 6703 syn_compare_syntime(const void *v1, const void *v2) 6704 { 6705 const time_entry_T *s1 = v1; 6706 const time_entry_T *s2 = v2; 6707 6708 return profile_cmp(&s1->total, &s2->total); 6709 } 6710 6711 /* 6712 * Clear the syntax timing for the current buffer. 6713 */ 6714 static void 6715 syntime_report(void) 6716 { 6717 int idx; 6718 synpat_T *spp; 6719 # ifdef FEAT_FLOAT 6720 proftime_T tm; 6721 # endif 6722 int len; 6723 proftime_T total_total; 6724 int total_count = 0; 6725 garray_T ga; 6726 time_entry_T *p; 6727 6728 if (!syntax_present(curwin)) 6729 { 6730 msg(_(msg_no_items)); 6731 return; 6732 } 6733 6734 ga_init2(&ga, sizeof(time_entry_T), 50); 6735 profile_zero(&total_total); 6736 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx) 6737 { 6738 spp = &(SYN_ITEMS(curwin->w_s)[idx]); 6739 if (spp->sp_time.count > 0) 6740 { 6741 (void)ga_grow(&ga, 1); 6742 p = ((time_entry_T *)ga.ga_data) + ga.ga_len; 6743 p->total = spp->sp_time.total; 6744 profile_add(&total_total, &spp->sp_time.total); 6745 p->count = spp->sp_time.count; 6746 p->match = spp->sp_time.match; 6747 total_count += spp->sp_time.count; 6748 p->slowest = spp->sp_time.slowest; 6749 # ifdef FEAT_FLOAT 6750 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm); 6751 p->average = tm; 6752 # endif 6753 p->id = spp->sp_syn.id; 6754 p->pattern = spp->sp_pattern; 6755 ++ga.ga_len; 6756 } 6757 } 6758 6759 /* Sort on total time. Skip if there are no items to avoid passing NULL 6760 * pointer to qsort(). */ 6761 if (ga.ga_len > 1) 6762 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T), 6763 syn_compare_syntime); 6764 6765 msg_puts_title(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN")); 6766 msg_puts("\n"); 6767 for (idx = 0; idx < ga.ga_len && !got_int; ++idx) 6768 { 6769 p = ((time_entry_T *)ga.ga_data) + idx; 6770 6771 msg_puts(profile_msg(&p->total)); 6772 msg_puts(" "); /* make sure there is always a separating space */ 6773 msg_advance(13); 6774 msg_outnum(p->count); 6775 msg_puts(" "); 6776 msg_advance(20); 6777 msg_outnum(p->match); 6778 msg_puts(" "); 6779 msg_advance(26); 6780 msg_puts(profile_msg(&p->slowest)); 6781 msg_puts(" "); 6782 msg_advance(38); 6783 # ifdef FEAT_FLOAT 6784 msg_puts(profile_msg(&p->average)); 6785 msg_puts(" "); 6786 # endif 6787 msg_advance(50); 6788 msg_outtrans(HL_TABLE()[p->id - 1].sg_name); 6789 msg_puts(" "); 6790 6791 msg_advance(69); 6792 if (Columns < 80) 6793 len = 20; /* will wrap anyway */ 6794 else 6795 len = Columns - 70; 6796 if (len > (int)STRLEN(p->pattern)) 6797 len = (int)STRLEN(p->pattern); 6798 msg_outtrans_len(p->pattern, len); 6799 msg_puts("\n"); 6800 } 6801 ga_clear(&ga); 6802 if (!got_int) 6803 { 6804 msg_puts("\n"); 6805 msg_puts(profile_msg(&total_total)); 6806 msg_advance(13); 6807 msg_outnum(total_count); 6808 msg_puts("\n"); 6809 } 6810 } 6811 #endif 6812 6813 #endif /* FEAT_SYN_HL */ 6814 6815 /************************************** 6816 * Highlighting stuff * 6817 **************************************/ 6818 6819 /* 6820 * The default highlight groups. These are compiled-in for fast startup and 6821 * they still work when the runtime files can't be found. 6822 * When making changes here, also change runtime/colors/default.vim! 6823 * The #ifdefs are needed to reduce the amount of static data. Helps to make 6824 * the 16 bit DOS (museum) version compile. 6825 */ 6826 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 6827 # define CENT(a, b) b 6828 #else 6829 # define CENT(a, b) a 6830 #endif 6831 static char *(highlight_init_both[]) = { 6832 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White", 6833 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"), 6834 CENT("IncSearch term=reverse cterm=reverse", 6835 "IncSearch term=reverse cterm=reverse gui=reverse"), 6836 CENT("ModeMsg term=bold cterm=bold", 6837 "ModeMsg term=bold cterm=bold gui=bold"), 6838 CENT("NonText term=bold ctermfg=Blue", 6839 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"), 6840 CENT("StatusLine term=reverse,bold cterm=reverse,bold", 6841 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"), 6842 CENT("StatusLineNC term=reverse cterm=reverse", 6843 "StatusLineNC term=reverse cterm=reverse gui=reverse"), 6844 "default link EndOfBuffer NonText", 6845 CENT("VertSplit term=reverse cterm=reverse", 6846 "VertSplit term=reverse cterm=reverse gui=reverse"), 6847 #ifdef FEAT_CLIPBOARD 6848 CENT("VisualNOS term=underline,bold cterm=underline,bold", 6849 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"), 6850 #endif 6851 #ifdef FEAT_DIFF 6852 CENT("DiffText term=reverse cterm=bold ctermbg=Red", 6853 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"), 6854 #endif 6855 #ifdef FEAT_INS_EXPAND 6856 CENT("PmenuSbar ctermbg=Grey", 6857 "PmenuSbar ctermbg=Grey guibg=Grey"), 6858 #endif 6859 CENT("TabLineSel term=bold cterm=bold", 6860 "TabLineSel term=bold cterm=bold gui=bold"), 6861 CENT("TabLineFill term=reverse cterm=reverse", 6862 "TabLineFill term=reverse cterm=reverse gui=reverse"), 6863 #ifdef FEAT_GUI 6864 "Cursor guibg=fg guifg=bg", 6865 "lCursor guibg=fg guifg=bg", /* should be different, but what? */ 6866 #endif 6867 "default link QuickFixLine Search", 6868 NULL 6869 }; 6870 6871 /* Default colors only used with a light background. */ 6872 static char *(highlight_init_light[]) = { 6873 CENT("Directory term=bold ctermfg=DarkBlue", 6874 "Directory term=bold ctermfg=DarkBlue guifg=Blue"), 6875 CENT("LineNr term=underline ctermfg=Brown", 6876 "LineNr term=underline ctermfg=Brown guifg=Brown"), 6877 CENT("CursorLineNr term=bold ctermfg=Brown", 6878 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"), 6879 CENT("MoreMsg term=bold ctermfg=DarkGreen", 6880 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"), 6881 CENT("Question term=standout ctermfg=DarkGreen", 6882 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"), 6883 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE", 6884 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"), 6885 #ifdef FEAT_SPELL 6886 CENT("SpellBad term=reverse ctermbg=LightRed", 6887 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"), 6888 CENT("SpellCap term=reverse ctermbg=LightBlue", 6889 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"), 6890 CENT("SpellRare term=reverse ctermbg=LightMagenta", 6891 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"), 6892 CENT("SpellLocal term=underline ctermbg=Cyan", 6893 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"), 6894 #endif 6895 #ifdef FEAT_INS_EXPAND 6896 CENT("PmenuThumb ctermbg=Black", 6897 "PmenuThumb ctermbg=Black guibg=Black"), 6898 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black", 6899 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"), 6900 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black", 6901 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"), 6902 #endif 6903 CENT("SpecialKey term=bold ctermfg=DarkBlue", 6904 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"), 6905 CENT("Title term=bold ctermfg=DarkMagenta", 6906 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"), 6907 CENT("WarningMsg term=standout ctermfg=DarkRed", 6908 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"), 6909 #ifdef FEAT_WILDMENU 6910 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black", 6911 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"), 6912 #endif 6913 #ifdef FEAT_FOLDING 6914 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue", 6915 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"), 6916 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue", 6917 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"), 6918 #endif 6919 #ifdef FEAT_SIGNS 6920 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue", 6921 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"), 6922 #endif 6923 CENT("Visual term=reverse", 6924 "Visual term=reverse guibg=LightGrey"), 6925 #ifdef FEAT_DIFF 6926 CENT("DiffAdd term=bold ctermbg=LightBlue", 6927 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"), 6928 CENT("DiffChange term=bold ctermbg=LightMagenta", 6929 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"), 6930 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan", 6931 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"), 6932 #endif 6933 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey", 6934 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"), 6935 #ifdef FEAT_SYN_HL 6936 CENT("CursorColumn term=reverse ctermbg=LightGrey", 6937 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"), 6938 CENT("CursorLine term=underline cterm=underline", 6939 "CursorLine term=underline cterm=underline guibg=Grey90"), 6940 CENT("ColorColumn term=reverse ctermbg=LightRed", 6941 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"), 6942 #endif 6943 #ifdef FEAT_CONCEAL 6944 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey", 6945 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"), 6946 #endif 6947 CENT("MatchParen term=reverse ctermbg=Cyan", 6948 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"), 6949 #ifdef FEAT_GUI 6950 "Normal gui=NONE", 6951 #endif 6952 #ifdef FEAT_TERMINAL 6953 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen", 6954 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"), 6955 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen", 6956 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"), 6957 #endif 6958 #ifdef FEAT_MENU 6959 CENT("ToolbarLine term=underline ctermbg=LightGrey", 6960 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"), 6961 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey", 6962 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"), 6963 #endif 6964 NULL 6965 }; 6966 6967 /* Default colors only used with a dark background. */ 6968 static char *(highlight_init_dark[]) = { 6969 CENT("Directory term=bold ctermfg=LightCyan", 6970 "Directory term=bold ctermfg=LightCyan guifg=Cyan"), 6971 CENT("LineNr term=underline ctermfg=Yellow", 6972 "LineNr term=underline ctermfg=Yellow guifg=Yellow"), 6973 CENT("CursorLineNr term=bold ctermfg=Yellow", 6974 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"), 6975 CENT("MoreMsg term=bold ctermfg=LightGreen", 6976 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"), 6977 CENT("Question term=standout ctermfg=LightGreen", 6978 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"), 6979 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black", 6980 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"), 6981 CENT("SpecialKey term=bold ctermfg=LightBlue", 6982 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"), 6983 #ifdef FEAT_SPELL 6984 CENT("SpellBad term=reverse ctermbg=Red", 6985 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"), 6986 CENT("SpellCap term=reverse ctermbg=Blue", 6987 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"), 6988 CENT("SpellRare term=reverse ctermbg=Magenta", 6989 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"), 6990 CENT("SpellLocal term=underline ctermbg=Cyan", 6991 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"), 6992 #endif 6993 #ifdef FEAT_INS_EXPAND 6994 CENT("PmenuThumb ctermbg=White", 6995 "PmenuThumb ctermbg=White guibg=White"), 6996 CENT("Pmenu ctermbg=Magenta ctermfg=Black", 6997 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"), 6998 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey", 6999 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"), 7000 #endif 7001 CENT("Title term=bold ctermfg=LightMagenta", 7002 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"), 7003 CENT("WarningMsg term=standout ctermfg=LightRed", 7004 "WarningMsg term=standout ctermfg=LightRed guifg=Red"), 7005 #ifdef FEAT_WILDMENU 7006 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black", 7007 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"), 7008 #endif 7009 #ifdef FEAT_FOLDING 7010 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan", 7011 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"), 7012 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan", 7013 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"), 7014 #endif 7015 #ifdef FEAT_SIGNS 7016 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan", 7017 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"), 7018 #endif 7019 CENT("Visual term=reverse", 7020 "Visual term=reverse guibg=DarkGrey"), 7021 #ifdef FEAT_DIFF 7022 CENT("DiffAdd term=bold ctermbg=DarkBlue", 7023 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"), 7024 CENT("DiffChange term=bold ctermbg=DarkMagenta", 7025 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"), 7026 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan", 7027 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"), 7028 #endif 7029 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey", 7030 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"), 7031 #ifdef FEAT_SYN_HL 7032 CENT("CursorColumn term=reverse ctermbg=DarkGrey", 7033 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"), 7034 CENT("CursorLine term=underline cterm=underline", 7035 "CursorLine term=underline cterm=underline guibg=Grey40"), 7036 CENT("ColorColumn term=reverse ctermbg=DarkRed", 7037 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"), 7038 #endif 7039 CENT("MatchParen term=reverse ctermbg=DarkCyan", 7040 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"), 7041 #ifdef FEAT_CONCEAL 7042 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey", 7043 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"), 7044 #endif 7045 #ifdef FEAT_GUI 7046 "Normal gui=NONE", 7047 #endif 7048 #ifdef FEAT_TERMINAL 7049 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen", 7050 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"), 7051 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen", 7052 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"), 7053 #endif 7054 #ifdef FEAT_MENU 7055 CENT("ToolbarLine term=underline ctermbg=DarkGrey", 7056 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"), 7057 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey", 7058 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"), 7059 #endif 7060 NULL 7061 }; 7062 7063 void 7064 init_highlight( 7065 int both, /* include groups where 'bg' doesn't matter */ 7066 int reset) /* clear group first */ 7067 { 7068 int i; 7069 char **pp; 7070 static int had_both = FALSE; 7071 #ifdef FEAT_EVAL 7072 char_u *p; 7073 7074 /* 7075 * Try finding the color scheme file. Used when a color file was loaded 7076 * and 'background' or 't_Co' is changed. 7077 */ 7078 p = get_var_value((char_u *)"g:colors_name"); 7079 if (p != NULL) 7080 { 7081 /* The value of g:colors_name could be freed when sourcing the script, 7082 * making "p" invalid, so copy it. */ 7083 char_u *copy_p = vim_strsave(p); 7084 int r; 7085 7086 if (copy_p != NULL) 7087 { 7088 r = load_colors(copy_p); 7089 vim_free(copy_p); 7090 if (r == OK) 7091 return; 7092 } 7093 } 7094 7095 #endif 7096 7097 /* 7098 * Didn't use a color file, use the compiled-in colors. 7099 */ 7100 if (both) 7101 { 7102 had_both = TRUE; 7103 pp = highlight_init_both; 7104 for (i = 0; pp[i] != NULL; ++i) 7105 do_highlight((char_u *)pp[i], reset, TRUE); 7106 } 7107 else if (!had_both) 7108 /* Don't do anything before the call with both == TRUE from main(). 7109 * Not everything has been setup then, and that call will overrule 7110 * everything anyway. */ 7111 return; 7112 7113 if (*p_bg == 'l') 7114 pp = highlight_init_light; 7115 else 7116 pp = highlight_init_dark; 7117 for (i = 0; pp[i] != NULL; ++i) 7118 do_highlight((char_u *)pp[i], reset, TRUE); 7119 7120 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it 7121 * depend on the number of colors available. 7122 * With 8 colors brown is equal to yellow, need to use black for Search fg 7123 * to avoid Statement highlighted text disappears. 7124 * Clear the attributes, needed when changing the t_Co value. */ 7125 if (t_colors > 8) 7126 do_highlight((char_u *)(*p_bg == 'l' 7127 ? "Visual cterm=NONE ctermbg=LightGrey" 7128 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE); 7129 else 7130 { 7131 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE", 7132 FALSE, TRUE); 7133 if (*p_bg == 'l') 7134 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE); 7135 } 7136 7137 #ifdef FEAT_SYN_HL 7138 /* 7139 * If syntax highlighting is enabled load the highlighting for it. 7140 */ 7141 if (get_var_value((char_u *)"g:syntax_on") != NULL) 7142 { 7143 static int recursive = 0; 7144 7145 if (recursive >= 5) 7146 emsg(_("E679: recursive loop loading syncolor.vim")); 7147 else 7148 { 7149 ++recursive; 7150 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL); 7151 --recursive; 7152 } 7153 } 7154 #endif 7155 } 7156 7157 /* 7158 * Load color file "name". 7159 * Return OK for success, FAIL for failure. 7160 */ 7161 int 7162 load_colors(char_u *name) 7163 { 7164 char_u *buf; 7165 int retval = FAIL; 7166 static int recursive = FALSE; 7167 7168 /* When being called recursively, this is probably because setting 7169 * 'background' caused the highlighting to be reloaded. This means it is 7170 * working, thus we should return OK. */ 7171 if (recursive) 7172 return OK; 7173 7174 recursive = TRUE; 7175 buf = alloc((unsigned)(STRLEN(name) + 12)); 7176 if (buf != NULL) 7177 { 7178 apply_autocmds(EVENT_COLORSCHEMEPRE, name, 7179 curbuf->b_fname, FALSE, curbuf); 7180 sprintf((char *)buf, "colors/%s.vim", name); 7181 retval = source_runtime(buf, DIP_START + DIP_OPT); 7182 vim_free(buf); 7183 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf); 7184 } 7185 recursive = FALSE; 7186 7187 return retval; 7188 } 7189 7190 static char *(color_names[28]) = { 7191 "Black", "DarkBlue", "DarkGreen", "DarkCyan", 7192 "DarkRed", "DarkMagenta", "Brown", "DarkYellow", 7193 "Gray", "Grey", "LightGray", "LightGrey", 7194 "DarkGray", "DarkGrey", 7195 "Blue", "LightBlue", "Green", "LightGreen", 7196 "Cyan", "LightCyan", "Red", "LightRed", "Magenta", 7197 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"}; 7198 /* indices: 7199 * 0, 1, 2, 3, 7200 * 4, 5, 6, 7, 7201 * 8, 9, 10, 11, 7202 * 12, 13, 7203 * 14, 15, 16, 17, 7204 * 18, 19, 20, 21, 22, 7205 * 23, 24, 25, 26, 27 */ 7206 static int color_numbers_16[28] = {0, 1, 2, 3, 7207 4, 5, 6, 6, 7208 7, 7, 7, 7, 7209 8, 8, 7210 9, 9, 10, 10, 7211 11, 11, 12, 12, 13, 7212 13, 14, 14, 15, -1}; 7213 /* for xterm with 88 colors... */ 7214 static int color_numbers_88[28] = {0, 4, 2, 6, 7215 1, 5, 32, 72, 7216 84, 84, 7, 7, 7217 82, 82, 7218 12, 43, 10, 61, 7219 14, 63, 9, 74, 13, 7220 75, 11, 78, 15, -1}; 7221 /* for xterm with 256 colors... */ 7222 static int color_numbers_256[28] = {0, 4, 2, 6, 7223 1, 5, 130, 130, 7224 248, 248, 7, 7, 7225 242, 242, 7226 12, 81, 10, 121, 7227 14, 159, 9, 224, 13, 7228 225, 11, 229, 15, -1}; 7229 /* for terminals with less than 16 colors... */ 7230 static int color_numbers_8[28] = {0, 4, 2, 6, 7231 1, 5, 3, 3, 7232 7, 7, 7, 7, 7233 0+8, 0+8, 7234 4+8, 4+8, 2+8, 2+8, 7235 6+8, 6+8, 1+8, 1+8, 5+8, 7236 5+8, 3+8, 3+8, 7+8, -1}; 7237 7238 /* 7239 * Lookup the "cterm" value to be used for color with index "idx" in 7240 * color_names[]. 7241 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8 7242 * colors, otherwise it will be unchanged. 7243 */ 7244 int 7245 lookup_color(int idx, int foreground, int *boldp) 7246 { 7247 int color = color_numbers_16[idx]; 7248 char_u *p; 7249 7250 /* Use the _16 table to check if it's a valid color name. */ 7251 if (color < 0) 7252 return -1; 7253 7254 if (t_colors == 8) 7255 { 7256 /* t_Co is 8: use the 8 colors table */ 7257 #if defined(__QNXNTO__) 7258 color = color_numbers_8_qansi[idx]; 7259 #else 7260 color = color_numbers_8[idx]; 7261 #endif 7262 if (foreground) 7263 { 7264 /* set/reset bold attribute to get light foreground 7265 * colors (on some terminals, e.g. "linux") */ 7266 if (color & 8) 7267 *boldp = TRUE; 7268 else 7269 *boldp = FALSE; 7270 } 7271 color &= 7; /* truncate to 8 colors */ 7272 } 7273 else if (t_colors == 16 || t_colors == 88 7274 || t_colors >= 256) 7275 { 7276 /* 7277 * Guess: if the termcap entry ends in 'm', it is 7278 * probably an xterm-like terminal. Use the changed 7279 * order for colors. 7280 */ 7281 if (*T_CAF != NUL) 7282 p = T_CAF; 7283 else 7284 p = T_CSF; 7285 if (*p != NUL && (t_colors > 256 7286 || *(p + STRLEN(p) - 1) == 'm')) 7287 { 7288 if (t_colors == 88) 7289 color = color_numbers_88[idx]; 7290 else if (t_colors >= 256) 7291 color = color_numbers_256[idx]; 7292 else 7293 color = color_numbers_8[idx]; 7294 } 7295 #ifdef FEAT_TERMRESPONSE 7296 if (t_colors >= 256 && color == 15 && is_mac_terminal) 7297 /* Terminal.app has a bug: 15 is light grey. Use white 7298 * from the color cube instead. */ 7299 color = 231; 7300 #endif 7301 } 7302 return color; 7303 } 7304 7305 /* 7306 * Handle the ":highlight .." command. 7307 * When using ":hi clear" this is called recursively for each group with 7308 * "forceit" and "init" both TRUE. 7309 */ 7310 void 7311 do_highlight( 7312 char_u *line, 7313 int forceit, 7314 int init) /* TRUE when called for initializing */ 7315 { 7316 char_u *name_end; 7317 char_u *p; 7318 char_u *linep; 7319 char_u *key_start; 7320 char_u *arg_start; 7321 char_u *key = NULL, *arg = NULL; 7322 long i; 7323 int off; 7324 int len; 7325 int attr; 7326 int id; 7327 int idx; 7328 struct hl_group item_before; 7329 int did_change = FALSE; 7330 int dodefault = FALSE; 7331 int doclear = FALSE; 7332 int dolink = FALSE; 7333 int error = FALSE; 7334 int color; 7335 int is_normal_group = FALSE; /* "Normal" group */ 7336 #ifdef FEAT_TERMINAL 7337 int is_terminal_group = FALSE; /* "Terminal" group */ 7338 #endif 7339 #ifdef FEAT_GUI_X11 7340 int is_menu_group = FALSE; /* "Menu" group */ 7341 int is_scrollbar_group = FALSE; /* "Scrollbar" group */ 7342 int is_tooltip_group = FALSE; /* "Tooltip" group */ 7343 int do_colors = FALSE; /* need to update colors? */ 7344 #else 7345 # define is_menu_group 0 7346 # define is_tooltip_group 0 7347 #endif 7348 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 7349 int did_highlight_changed = FALSE; 7350 #endif 7351 7352 /* 7353 * If no argument, list current highlighting. 7354 */ 7355 if (ends_excmd(*line)) 7356 { 7357 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i) 7358 /* TODO: only call when the group has attributes set */ 7359 highlight_list_one((int)i); 7360 return; 7361 } 7362 7363 /* 7364 * Isolate the name. 7365 */ 7366 name_end = skiptowhite(line); 7367 linep = skipwhite(name_end); 7368 7369 /* 7370 * Check for "default" argument. 7371 */ 7372 if (STRNCMP(line, "default", name_end - line) == 0) 7373 { 7374 dodefault = TRUE; 7375 line = linep; 7376 name_end = skiptowhite(line); 7377 linep = skipwhite(name_end); 7378 } 7379 7380 /* 7381 * Check for "clear" or "link" argument. 7382 */ 7383 if (STRNCMP(line, "clear", name_end - line) == 0) 7384 doclear = TRUE; 7385 if (STRNCMP(line, "link", name_end - line) == 0) 7386 dolink = TRUE; 7387 7388 /* 7389 * ":highlight {group-name}": list highlighting for one group. 7390 */ 7391 if (!doclear && !dolink && ends_excmd(*linep)) 7392 { 7393 id = syn_namen2id(line, (int)(name_end - line)); 7394 if (id == 0) 7395 semsg(_("E411: highlight group not found: %s"), line); 7396 else 7397 highlight_list_one(id); 7398 return; 7399 } 7400 7401 /* 7402 * Handle ":highlight link {from} {to}" command. 7403 */ 7404 if (dolink) 7405 { 7406 char_u *from_start = linep; 7407 char_u *from_end; 7408 char_u *to_start; 7409 char_u *to_end; 7410 int from_id; 7411 int to_id; 7412 7413 from_end = skiptowhite(from_start); 7414 to_start = skipwhite(from_end); 7415 to_end = skiptowhite(to_start); 7416 7417 if (ends_excmd(*from_start) || ends_excmd(*to_start)) 7418 { 7419 semsg(_("E412: Not enough arguments: \":highlight link %s\""), 7420 from_start); 7421 return; 7422 } 7423 7424 if (!ends_excmd(*skipwhite(to_end))) 7425 { 7426 semsg(_("E413: Too many arguments: \":highlight link %s\""), from_start); 7427 return; 7428 } 7429 7430 from_id = syn_check_group(from_start, (int)(from_end - from_start)); 7431 if (STRNCMP(to_start, "NONE", 4) == 0) 7432 to_id = 0; 7433 else 7434 to_id = syn_check_group(to_start, (int)(to_end - to_start)); 7435 7436 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0)) 7437 { 7438 /* 7439 * Don't allow a link when there already is some highlighting 7440 * for the group, unless '!' is used 7441 */ 7442 if (to_id > 0 && !forceit && !init 7443 && hl_has_settings(from_id - 1, dodefault)) 7444 { 7445 if (sourcing_name == NULL && !dodefault) 7446 emsg(_("E414: group has settings, highlight link ignored")); 7447 } 7448 else if (HL_TABLE()[from_id - 1].sg_link != to_id 7449 #ifdef FEAT_EVAL 7450 || HL_TABLE()[from_id - 1].sg_script_ctx.sc_sid 7451 != current_sctx.sc_sid 7452 #endif 7453 || HL_TABLE()[from_id - 1].sg_cleared) 7454 { 7455 if (!init) 7456 HL_TABLE()[from_id - 1].sg_set |= SG_LINK; 7457 HL_TABLE()[from_id - 1].sg_link = to_id; 7458 #ifdef FEAT_EVAL 7459 HL_TABLE()[from_id - 1].sg_script_ctx = current_sctx; 7460 HL_TABLE()[from_id - 1].sg_script_ctx.sc_lnum += sourcing_lnum; 7461 #endif 7462 HL_TABLE()[from_id - 1].sg_cleared = FALSE; 7463 redraw_all_later(SOME_VALID); 7464 7465 /* Only call highlight_changed() once after multiple changes. */ 7466 need_highlight_changed = TRUE; 7467 } 7468 } 7469 7470 return; 7471 } 7472 7473 if (doclear) 7474 { 7475 /* 7476 * ":highlight clear [group]" command. 7477 */ 7478 line = linep; 7479 if (ends_excmd(*line)) 7480 { 7481 #ifdef FEAT_GUI 7482 /* First, we do not destroy the old values, but allocate the new 7483 * ones and update the display. THEN we destroy the old values. 7484 * If we destroy the old values first, then the old values 7485 * (such as GuiFont's or GuiFontset's) will still be displayed but 7486 * invalid because they were free'd. 7487 */ 7488 if (gui.in_use) 7489 { 7490 # ifdef FEAT_BEVAL_TIP 7491 gui_init_tooltip_font(); 7492 # endif 7493 # if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF)) 7494 gui_init_menu_font(); 7495 # endif 7496 } 7497 # if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11) 7498 gui_mch_def_colors(); 7499 # endif 7500 # ifdef FEAT_GUI_X11 7501 # ifdef FEAT_MENU 7502 7503 /* This only needs to be done when there is no Menu highlight 7504 * group defined by default, which IS currently the case. 7505 */ 7506 gui_mch_new_menu_colors(); 7507 # endif 7508 if (gui.in_use) 7509 { 7510 gui_new_scrollbar_colors(); 7511 # ifdef FEAT_BEVAL_GUI 7512 gui_mch_new_tooltip_colors(); 7513 # endif 7514 # ifdef FEAT_MENU 7515 gui_mch_new_menu_font(); 7516 # endif 7517 } 7518 # endif 7519 7520 /* Ok, we're done allocating the new default graphics items. 7521 * The screen should already be refreshed at this point. 7522 * It is now Ok to clear out the old data. 7523 */ 7524 #endif 7525 #ifdef FEAT_EVAL 7526 do_unlet((char_u *)"colors_name", TRUE); 7527 #endif 7528 restore_cterm_colors(); 7529 7530 /* 7531 * Clear all default highlight groups and load the defaults. 7532 */ 7533 for (idx = 0; idx < highlight_ga.ga_len; ++idx) 7534 highlight_clear(idx); 7535 init_highlight(TRUE, TRUE); 7536 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 7537 if (USE_24BIT) 7538 highlight_gui_started(); 7539 else 7540 #endif 7541 highlight_changed(); 7542 redraw_later_clear(); 7543 return; 7544 } 7545 name_end = skiptowhite(line); 7546 linep = skipwhite(name_end); 7547 } 7548 7549 /* 7550 * Find the group name in the table. If it does not exist yet, add it. 7551 */ 7552 id = syn_check_group(line, (int)(name_end - line)); 7553 if (id == 0) /* failed (out of memory) */ 7554 return; 7555 idx = id - 1; /* index is ID minus one */ 7556 7557 /* Return if "default" was used and the group already has settings. */ 7558 if (dodefault && hl_has_settings(idx, TRUE)) 7559 return; 7560 7561 /* Make a copy so we can check if any attribute actually changed. */ 7562 item_before = HL_TABLE()[idx]; 7563 7564 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0) 7565 is_normal_group = TRUE; 7566 #ifdef FEAT_TERMINAL 7567 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TERMINAL") == 0) 7568 is_terminal_group = TRUE; 7569 #endif 7570 #ifdef FEAT_GUI_X11 7571 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0) 7572 is_menu_group = TRUE; 7573 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0) 7574 is_scrollbar_group = TRUE; 7575 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0) 7576 is_tooltip_group = TRUE; 7577 #endif 7578 7579 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */ 7580 if (doclear || (forceit && init)) 7581 { 7582 highlight_clear(idx); 7583 if (!doclear) 7584 HL_TABLE()[idx].sg_set = 0; 7585 } 7586 7587 if (!doclear) 7588 while (!ends_excmd(*linep)) 7589 { 7590 key_start = linep; 7591 if (*linep == '=') 7592 { 7593 semsg(_("E415: unexpected equal sign: %s"), key_start); 7594 error = TRUE; 7595 break; 7596 } 7597 7598 /* 7599 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or 7600 * "guibg"). 7601 */ 7602 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=') 7603 ++linep; 7604 vim_free(key); 7605 key = vim_strnsave_up(key_start, (int)(linep - key_start)); 7606 if (key == NULL) 7607 { 7608 error = TRUE; 7609 break; 7610 } 7611 linep = skipwhite(linep); 7612 7613 if (STRCMP(key, "NONE") == 0) 7614 { 7615 if (!init || HL_TABLE()[idx].sg_set == 0) 7616 { 7617 if (!init) 7618 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI; 7619 highlight_clear(idx); 7620 } 7621 continue; 7622 } 7623 7624 /* 7625 * Check for the equal sign. 7626 */ 7627 if (*linep != '=') 7628 { 7629 semsg(_("E416: missing equal sign: %s"), key_start); 7630 error = TRUE; 7631 break; 7632 } 7633 ++linep; 7634 7635 /* 7636 * Isolate the argument. 7637 */ 7638 linep = skipwhite(linep); 7639 if (*linep == '\'') /* guifg='color name' */ 7640 { 7641 arg_start = ++linep; 7642 linep = vim_strchr(linep, '\''); 7643 if (linep == NULL) 7644 { 7645 semsg(_(e_invarg2), key_start); 7646 error = TRUE; 7647 break; 7648 } 7649 } 7650 else 7651 { 7652 arg_start = linep; 7653 linep = skiptowhite(linep); 7654 } 7655 if (linep == arg_start) 7656 { 7657 semsg(_("E417: missing argument: %s"), key_start); 7658 error = TRUE; 7659 break; 7660 } 7661 vim_free(arg); 7662 arg = vim_strnsave(arg_start, (int)(linep - arg_start)); 7663 if (arg == NULL) 7664 { 7665 error = TRUE; 7666 break; 7667 } 7668 if (*linep == '\'') 7669 ++linep; 7670 7671 /* 7672 * Store the argument. 7673 */ 7674 if ( STRCMP(key, "TERM") == 0 7675 || STRCMP(key, "CTERM") == 0 7676 || STRCMP(key, "GUI") == 0) 7677 { 7678 attr = 0; 7679 off = 0; 7680 while (arg[off] != NUL) 7681 { 7682 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; ) 7683 { 7684 len = (int)STRLEN(hl_name_table[i]); 7685 if (STRNICMP(arg + off, hl_name_table[i], len) == 0) 7686 { 7687 attr |= hl_attr_table[i]; 7688 off += len; 7689 break; 7690 } 7691 } 7692 if (i < 0) 7693 { 7694 semsg(_("E418: Illegal value: %s"), arg); 7695 error = TRUE; 7696 break; 7697 } 7698 if (arg[off] == ',') /* another one follows */ 7699 ++off; 7700 } 7701 if (error) 7702 break; 7703 if (*key == 'T') 7704 { 7705 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM)) 7706 { 7707 if (!init) 7708 HL_TABLE()[idx].sg_set |= SG_TERM; 7709 HL_TABLE()[idx].sg_term = attr; 7710 } 7711 } 7712 else if (*key == 'C') 7713 { 7714 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) 7715 { 7716 if (!init) 7717 HL_TABLE()[idx].sg_set |= SG_CTERM; 7718 HL_TABLE()[idx].sg_cterm = attr; 7719 HL_TABLE()[idx].sg_cterm_bold = FALSE; 7720 } 7721 } 7722 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 7723 else 7724 { 7725 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) 7726 { 7727 if (!init) 7728 HL_TABLE()[idx].sg_set |= SG_GUI; 7729 HL_TABLE()[idx].sg_gui = attr; 7730 } 7731 } 7732 #endif 7733 } 7734 else if (STRCMP(key, "FONT") == 0) 7735 { 7736 /* in non-GUI fonts are simply ignored */ 7737 #ifdef FEAT_GUI 7738 if (HL_TABLE()[idx].sg_font_name != NULL 7739 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0) 7740 { 7741 /* Font name didn't change, ignore. */ 7742 } 7743 else if (!gui.shell_created) 7744 { 7745 /* GUI not started yet, always accept the name. */ 7746 vim_free(HL_TABLE()[idx].sg_font_name); 7747 HL_TABLE()[idx].sg_font_name = vim_strsave(arg); 7748 did_change = TRUE; 7749 } 7750 else 7751 { 7752 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font; 7753 # ifdef FEAT_XFONTSET 7754 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset; 7755 # endif 7756 /* First, save the current font/fontset. 7757 * Then try to allocate the font/fontset. 7758 * If the allocation fails, HL_TABLE()[idx].sg_font OR 7759 * sg_fontset will be set to NOFONT or NOFONTSET respectively. 7760 */ 7761 7762 HL_TABLE()[idx].sg_font = NOFONT; 7763 # ifdef FEAT_XFONTSET 7764 HL_TABLE()[idx].sg_fontset = NOFONTSET; 7765 # endif 7766 hl_do_font(idx, arg, is_normal_group, is_menu_group, 7767 is_tooltip_group, FALSE); 7768 7769 # ifdef FEAT_XFONTSET 7770 if (HL_TABLE()[idx].sg_fontset != NOFONTSET) 7771 { 7772 /* New fontset was accepted. Free the old one, if there 7773 * was one. */ 7774 gui_mch_free_fontset(temp_sg_fontset); 7775 vim_free(HL_TABLE()[idx].sg_font_name); 7776 HL_TABLE()[idx].sg_font_name = vim_strsave(arg); 7777 did_change = TRUE; 7778 } 7779 else 7780 HL_TABLE()[idx].sg_fontset = temp_sg_fontset; 7781 # endif 7782 if (HL_TABLE()[idx].sg_font != NOFONT) 7783 { 7784 /* New font was accepted. Free the old one, if there was 7785 * one. */ 7786 gui_mch_free_font(temp_sg_font); 7787 vim_free(HL_TABLE()[idx].sg_font_name); 7788 HL_TABLE()[idx].sg_font_name = vim_strsave(arg); 7789 did_change = TRUE; 7790 } 7791 else 7792 HL_TABLE()[idx].sg_font = temp_sg_font; 7793 } 7794 #endif 7795 } 7796 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) 7797 { 7798 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) 7799 { 7800 if (!init) 7801 HL_TABLE()[idx].sg_set |= SG_CTERM; 7802 7803 /* When setting the foreground color, and previously the "bold" 7804 * flag was set for a light color, reset it now */ 7805 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold) 7806 { 7807 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; 7808 HL_TABLE()[idx].sg_cterm_bold = FALSE; 7809 } 7810 7811 if (VIM_ISDIGIT(*arg)) 7812 color = atoi((char *)arg); 7813 else if (STRICMP(arg, "fg") == 0) 7814 { 7815 if (cterm_normal_fg_color) 7816 color = cterm_normal_fg_color - 1; 7817 else 7818 { 7819 emsg(_("E419: FG color unknown")); 7820 error = TRUE; 7821 break; 7822 } 7823 } 7824 else if (STRICMP(arg, "bg") == 0) 7825 { 7826 if (cterm_normal_bg_color > 0) 7827 color = cterm_normal_bg_color - 1; 7828 else 7829 { 7830 emsg(_("E420: BG color unknown")); 7831 error = TRUE; 7832 break; 7833 } 7834 } 7835 else 7836 { 7837 int bold = MAYBE; 7838 7839 #if defined(__QNXNTO__) 7840 static int *color_numbers_8_qansi = color_numbers_8; 7841 /* On qnx, the 8 & 16 color arrays are the same */ 7842 if (STRNCMP(T_NAME, "qansi", 5) == 0) 7843 color_numbers_8_qansi = color_numbers_16; 7844 #endif 7845 7846 /* reduce calls to STRICMP a bit, it can be slow */ 7847 off = TOUPPER_ASC(*arg); 7848 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; ) 7849 if (off == color_names[i][0] 7850 && STRICMP(arg + 1, color_names[i] + 1) == 0) 7851 break; 7852 if (i < 0) 7853 { 7854 semsg(_("E421: Color name or number not recognized: %s"), key_start); 7855 error = TRUE; 7856 break; 7857 } 7858 7859 color = lookup_color(i, key[5] == 'F', &bold); 7860 7861 /* set/reset bold attribute to get light foreground 7862 * colors (on some terminals, e.g. "linux") */ 7863 if (bold == TRUE) 7864 { 7865 HL_TABLE()[idx].sg_cterm |= HL_BOLD; 7866 HL_TABLE()[idx].sg_cterm_bold = TRUE; 7867 } 7868 else if (bold == FALSE) 7869 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; 7870 } 7871 7872 /* Add one to the argument, to avoid zero. Zero is used for 7873 * "NONE", then "color" is -1. */ 7874 if (key[5] == 'F') 7875 { 7876 HL_TABLE()[idx].sg_cterm_fg = color + 1; 7877 if (is_normal_group) 7878 { 7879 cterm_normal_fg_color = color + 1; 7880 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD); 7881 #ifdef FEAT_GUI 7882 /* Don't do this if the GUI is used. */ 7883 if (!gui.in_use && !gui.starting) 7884 #endif 7885 { 7886 must_redraw = CLEAR; 7887 if (termcap_active && color >= 0) 7888 term_fg_color(color); 7889 } 7890 } 7891 } 7892 else 7893 { 7894 HL_TABLE()[idx].sg_cterm_bg = color + 1; 7895 if (is_normal_group) 7896 { 7897 cterm_normal_bg_color = color + 1; 7898 #ifdef FEAT_GUI 7899 /* Don't mess with 'background' if the GUI is used. */ 7900 if (!gui.in_use && !gui.starting) 7901 #endif 7902 { 7903 must_redraw = CLEAR; 7904 if (color >= 0) 7905 { 7906 int dark = -1; 7907 7908 if (termcap_active) 7909 term_bg_color(color); 7910 if (t_colors < 16) 7911 dark = (color == 0 || color == 4); 7912 /* Limit the heuristic to the standard 16 colors */ 7913 else if (color < 16) 7914 dark = (color < 7 || color == 8); 7915 /* Set the 'background' option if the value is 7916 * wrong. */ 7917 if (dark != -1 7918 && dark != (*p_bg == 'd') 7919 && !option_was_set((char_u *)"bg")) 7920 { 7921 set_option_value((char_u *)"bg", 0L, 7922 (char_u *)(dark ? "dark" : "light"), 0); 7923 reset_option_was_set((char_u *)"bg"); 7924 } 7925 } 7926 } 7927 } 7928 } 7929 } 7930 } 7931 else if (STRCMP(key, "GUIFG") == 0) 7932 { 7933 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 7934 char_u **namep = &HL_TABLE()[idx].sg_gui_fg_name; 7935 7936 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) 7937 { 7938 if (!init) 7939 HL_TABLE()[idx].sg_set |= SG_GUI; 7940 7941 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 7942 /* In GUI guifg colors are only used when recognized */ 7943 i = color_name2handle(arg); 7944 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT) 7945 { 7946 HL_TABLE()[idx].sg_gui_fg = i; 7947 # endif 7948 if (*namep == NULL || STRCMP(*namep, arg) != 0) 7949 { 7950 vim_free(*namep); 7951 if (STRCMP(arg, "NONE") != 0) 7952 *namep = vim_strsave(arg); 7953 else 7954 *namep = NULL; 7955 did_change = TRUE; 7956 } 7957 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 7958 # ifdef FEAT_GUI_X11 7959 if (is_menu_group && gui.menu_fg_pixel != i) 7960 { 7961 gui.menu_fg_pixel = i; 7962 do_colors = TRUE; 7963 } 7964 if (is_scrollbar_group && gui.scroll_fg_pixel != i) 7965 { 7966 gui.scroll_fg_pixel = i; 7967 do_colors = TRUE; 7968 } 7969 # ifdef FEAT_BEVAL_GUI 7970 if (is_tooltip_group && gui.tooltip_fg_pixel != i) 7971 { 7972 gui.tooltip_fg_pixel = i; 7973 do_colors = TRUE; 7974 } 7975 # endif 7976 # endif 7977 } 7978 # endif 7979 } 7980 #endif 7981 } 7982 else if (STRCMP(key, "GUIBG") == 0) 7983 { 7984 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 7985 char_u **namep = &HL_TABLE()[idx].sg_gui_bg_name; 7986 7987 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) 7988 { 7989 if (!init) 7990 HL_TABLE()[idx].sg_set |= SG_GUI; 7991 7992 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 7993 /* In GUI guifg colors are only used when recognized */ 7994 i = color_name2handle(arg); 7995 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT) 7996 { 7997 HL_TABLE()[idx].sg_gui_bg = i; 7998 # endif 7999 if (*namep == NULL || STRCMP(*namep, arg) != 0) 8000 { 8001 vim_free(*namep); 8002 if (STRCMP(arg, "NONE") != 0) 8003 *namep = vim_strsave(arg); 8004 else 8005 *namep = NULL; 8006 did_change = TRUE; 8007 } 8008 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 8009 # ifdef FEAT_GUI_X11 8010 if (is_menu_group && gui.menu_bg_pixel != i) 8011 { 8012 gui.menu_bg_pixel = i; 8013 do_colors = TRUE; 8014 } 8015 if (is_scrollbar_group && gui.scroll_bg_pixel != i) 8016 { 8017 gui.scroll_bg_pixel = i; 8018 do_colors = TRUE; 8019 } 8020 # ifdef FEAT_BEVAL_GUI 8021 if (is_tooltip_group && gui.tooltip_bg_pixel != i) 8022 { 8023 gui.tooltip_bg_pixel = i; 8024 do_colors = TRUE; 8025 } 8026 # endif 8027 # endif 8028 } 8029 # endif 8030 } 8031 #endif 8032 } 8033 else if (STRCMP(key, "GUISP") == 0) 8034 { 8035 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 8036 char_u **namep = &HL_TABLE()[idx].sg_gui_sp_name; 8037 8038 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) 8039 { 8040 if (!init) 8041 HL_TABLE()[idx].sg_set |= SG_GUI; 8042 8043 # ifdef FEAT_GUI 8044 i = color_name2handle(arg); 8045 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use) 8046 { 8047 HL_TABLE()[idx].sg_gui_sp = i; 8048 # endif 8049 if (*namep == NULL || STRCMP(*namep, arg) != 0) 8050 { 8051 vim_free(*namep); 8052 if (STRCMP(arg, "NONE") != 0) 8053 *namep = vim_strsave(arg); 8054 else 8055 *namep = NULL; 8056 did_change = TRUE; 8057 } 8058 # ifdef FEAT_GUI 8059 } 8060 # endif 8061 } 8062 #endif 8063 } 8064 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0) 8065 { 8066 char_u buf[100]; 8067 char_u *tname; 8068 8069 if (!init) 8070 HL_TABLE()[idx].sg_set |= SG_TERM; 8071 8072 /* 8073 * The "start" and "stop" arguments can be a literal escape 8074 * sequence, or a comma separated list of terminal codes. 8075 */ 8076 if (STRNCMP(arg, "t_", 2) == 0) 8077 { 8078 off = 0; 8079 buf[0] = 0; 8080 while (arg[off] != NUL) 8081 { 8082 /* Isolate one termcap name */ 8083 for (len = 0; arg[off + len] && 8084 arg[off + len] != ','; ++len) 8085 ; 8086 tname = vim_strnsave(arg + off, len); 8087 if (tname == NULL) /* out of memory */ 8088 { 8089 error = TRUE; 8090 break; 8091 } 8092 /* lookup the escape sequence for the item */ 8093 p = get_term_code(tname); 8094 vim_free(tname); 8095 if (p == NULL) /* ignore non-existing things */ 8096 p = (char_u *)""; 8097 8098 /* Append it to the already found stuff */ 8099 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99) 8100 { 8101 semsg(_("E422: terminal code too long: %s"), arg); 8102 error = TRUE; 8103 break; 8104 } 8105 STRCAT(buf, p); 8106 8107 /* Advance to the next item */ 8108 off += len; 8109 if (arg[off] == ',') /* another one follows */ 8110 ++off; 8111 } 8112 } 8113 else 8114 { 8115 /* 8116 * Copy characters from arg[] to buf[], translating <> codes. 8117 */ 8118 for (p = arg, off = 0; off < 100 - 6 && *p; ) 8119 { 8120 len = trans_special(&p, buf + off, FALSE, FALSE); 8121 if (len > 0) /* recognized special char */ 8122 off += len; 8123 else /* copy as normal char */ 8124 buf[off++] = *p++; 8125 } 8126 buf[off] = NUL; 8127 } 8128 if (error) 8129 break; 8130 8131 if (STRCMP(buf, "NONE") == 0) /* resetting the value */ 8132 p = NULL; 8133 else 8134 p = vim_strsave(buf); 8135 if (key[2] == 'A') 8136 { 8137 vim_free(HL_TABLE()[idx].sg_start); 8138 HL_TABLE()[idx].sg_start = p; 8139 } 8140 else 8141 { 8142 vim_free(HL_TABLE()[idx].sg_stop); 8143 HL_TABLE()[idx].sg_stop = p; 8144 } 8145 } 8146 else 8147 { 8148 semsg(_("E423: Illegal argument: %s"), key_start); 8149 error = TRUE; 8150 break; 8151 } 8152 HL_TABLE()[idx].sg_cleared = FALSE; 8153 8154 /* 8155 * When highlighting has been given for a group, don't link it. 8156 */ 8157 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK)) 8158 HL_TABLE()[idx].sg_link = 0; 8159 8160 /* 8161 * Continue with next argument. 8162 */ 8163 linep = skipwhite(linep); 8164 } 8165 8166 /* 8167 * If there is an error, and it's a new entry, remove it from the table. 8168 */ 8169 if (error && idx == highlight_ga.ga_len) 8170 syn_unadd_group(); 8171 else 8172 { 8173 if (is_normal_group) 8174 { 8175 HL_TABLE()[idx].sg_term_attr = 0; 8176 HL_TABLE()[idx].sg_cterm_attr = 0; 8177 #ifdef FEAT_GUI 8178 HL_TABLE()[idx].sg_gui_attr = 0; 8179 /* 8180 * Need to update all groups, because they might be using "bg" 8181 * and/or "fg", which have been changed now. 8182 */ 8183 #endif 8184 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 8185 if (USE_24BIT) 8186 { 8187 highlight_gui_started(); 8188 did_highlight_changed = TRUE; 8189 redraw_all_later(NOT_VALID); 8190 } 8191 #endif 8192 } 8193 #ifdef FEAT_TERMINAL 8194 else if (is_terminal_group) 8195 set_terminal_default_colors( 8196 HL_TABLE()[idx].sg_cterm_fg, HL_TABLE()[idx].sg_cterm_bg); 8197 #endif 8198 #ifdef FEAT_GUI_X11 8199 # ifdef FEAT_MENU 8200 else if (is_menu_group) 8201 { 8202 if (gui.in_use && do_colors) 8203 gui_mch_new_menu_colors(); 8204 } 8205 # endif 8206 else if (is_scrollbar_group) 8207 { 8208 if (gui.in_use && do_colors) 8209 gui_new_scrollbar_colors(); 8210 } 8211 # ifdef FEAT_BEVAL_GUI 8212 else if (is_tooltip_group) 8213 { 8214 if (gui.in_use && do_colors) 8215 gui_mch_new_tooltip_colors(); 8216 } 8217 # endif 8218 #endif 8219 else 8220 set_hl_attr(idx); 8221 #ifdef FEAT_EVAL 8222 HL_TABLE()[idx].sg_script_ctx = current_sctx; 8223 HL_TABLE()[idx].sg_script_ctx.sc_lnum += sourcing_lnum; 8224 #endif 8225 } 8226 8227 vim_free(key); 8228 vim_free(arg); 8229 8230 /* Only call highlight_changed() once, after a sequence of highlight 8231 * commands, and only if an attribute actually changed. */ 8232 if ((did_change 8233 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0) 8234 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 8235 && !did_highlight_changed 8236 #endif 8237 ) 8238 { 8239 /* Do not trigger a redraw when highlighting is changed while 8240 * redrawing. This may happen when evaluating 'statusline' changes the 8241 * StatusLine group. */ 8242 if (!updating_screen) 8243 redraw_all_later(NOT_VALID); 8244 need_highlight_changed = TRUE; 8245 } 8246 } 8247 8248 #if defined(EXITFREE) || defined(PROTO) 8249 void 8250 free_highlight(void) 8251 { 8252 int i; 8253 8254 for (i = 0; i < highlight_ga.ga_len; ++i) 8255 { 8256 highlight_clear(i); 8257 vim_free(HL_TABLE()[i].sg_name); 8258 vim_free(HL_TABLE()[i].sg_name_u); 8259 } 8260 ga_clear(&highlight_ga); 8261 } 8262 #endif 8263 8264 /* 8265 * Reset the cterm colors to what they were before Vim was started, if 8266 * possible. Otherwise reset them to zero. 8267 */ 8268 void 8269 restore_cterm_colors(void) 8270 { 8271 #if defined(MSWIN) && !defined(FEAT_GUI_MSWIN) 8272 /* Since t_me has been set, this probably means that the user 8273 * wants to use this as default colors. Need to reset default 8274 * background/foreground colors. */ 8275 mch_set_normal_colors(); 8276 #else 8277 # ifdef VIMDLL 8278 if (!gui.in_use) 8279 { 8280 mch_set_normal_colors(); 8281 return; 8282 } 8283 # endif 8284 cterm_normal_fg_color = 0; 8285 cterm_normal_fg_bold = 0; 8286 cterm_normal_bg_color = 0; 8287 # ifdef FEAT_TERMGUICOLORS 8288 cterm_normal_fg_gui_color = INVALCOLOR; 8289 cterm_normal_bg_gui_color = INVALCOLOR; 8290 # endif 8291 #endif 8292 } 8293 8294 /* 8295 * Return TRUE if highlight group "idx" has any settings. 8296 * When "check_link" is TRUE also check for an existing link. 8297 */ 8298 static int 8299 hl_has_settings(int idx, int check_link) 8300 { 8301 return ( HL_TABLE()[idx].sg_term_attr != 0 8302 || HL_TABLE()[idx].sg_cterm_attr != 0 8303 || HL_TABLE()[idx].sg_cterm_fg != 0 8304 || HL_TABLE()[idx].sg_cterm_bg != 0 8305 #ifdef FEAT_GUI 8306 || HL_TABLE()[idx].sg_gui_attr != 0 8307 || HL_TABLE()[idx].sg_gui_fg_name != NULL 8308 || HL_TABLE()[idx].sg_gui_bg_name != NULL 8309 || HL_TABLE()[idx].sg_gui_sp_name != NULL 8310 || HL_TABLE()[idx].sg_font_name != NULL 8311 #endif 8312 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK))); 8313 } 8314 8315 /* 8316 * Clear highlighting for one group. 8317 */ 8318 static void 8319 highlight_clear(int idx) 8320 { 8321 HL_TABLE()[idx].sg_cleared = TRUE; 8322 8323 HL_TABLE()[idx].sg_term = 0; 8324 VIM_CLEAR(HL_TABLE()[idx].sg_start); 8325 VIM_CLEAR(HL_TABLE()[idx].sg_stop); 8326 HL_TABLE()[idx].sg_term_attr = 0; 8327 HL_TABLE()[idx].sg_cterm = 0; 8328 HL_TABLE()[idx].sg_cterm_bold = FALSE; 8329 HL_TABLE()[idx].sg_cterm_fg = 0; 8330 HL_TABLE()[idx].sg_cterm_bg = 0; 8331 HL_TABLE()[idx].sg_cterm_attr = 0; 8332 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 8333 HL_TABLE()[idx].sg_gui = 0; 8334 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name); 8335 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name); 8336 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name); 8337 #endif 8338 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 8339 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR; 8340 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR; 8341 #endif 8342 #ifdef FEAT_GUI 8343 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR; 8344 gui_mch_free_font(HL_TABLE()[idx].sg_font); 8345 HL_TABLE()[idx].sg_font = NOFONT; 8346 # ifdef FEAT_XFONTSET 8347 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset); 8348 HL_TABLE()[idx].sg_fontset = NOFONTSET; 8349 # endif 8350 VIM_CLEAR(HL_TABLE()[idx].sg_font_name); 8351 HL_TABLE()[idx].sg_gui_attr = 0; 8352 #endif 8353 #ifdef FEAT_EVAL 8354 /* Clear the script ID only when there is no link, since that is not 8355 * cleared. */ 8356 if (HL_TABLE()[idx].sg_link == 0) 8357 { 8358 HL_TABLE()[idx].sg_script_ctx.sc_sid = 0; 8359 HL_TABLE()[idx].sg_script_ctx.sc_lnum = 0; 8360 } 8361 #endif 8362 } 8363 8364 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO) 8365 /* 8366 * Set the normal foreground and background colors according to the "Normal" 8367 * highlighting group. For X11 also set "Menu", "Scrollbar", and 8368 * "Tooltip" colors. 8369 */ 8370 void 8371 set_normal_colors(void) 8372 { 8373 # ifdef FEAT_GUI 8374 # ifdef FEAT_TERMGUICOLORS 8375 if (gui.in_use) 8376 # endif 8377 { 8378 if (set_group_colors((char_u *)"Normal", 8379 &gui.norm_pixel, &gui.back_pixel, 8380 FALSE, TRUE, FALSE)) 8381 { 8382 gui_mch_new_colors(); 8383 must_redraw = CLEAR; 8384 } 8385 # ifdef FEAT_GUI_X11 8386 if (set_group_colors((char_u *)"Menu", 8387 &gui.menu_fg_pixel, &gui.menu_bg_pixel, 8388 TRUE, FALSE, FALSE)) 8389 { 8390 # ifdef FEAT_MENU 8391 gui_mch_new_menu_colors(); 8392 # endif 8393 must_redraw = CLEAR; 8394 } 8395 # ifdef FEAT_BEVAL_GUI 8396 if (set_group_colors((char_u *)"Tooltip", 8397 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel, 8398 FALSE, FALSE, TRUE)) 8399 { 8400 # ifdef FEAT_TOOLBAR 8401 gui_mch_new_tooltip_colors(); 8402 # endif 8403 must_redraw = CLEAR; 8404 } 8405 # endif 8406 if (set_group_colors((char_u *)"Scrollbar", 8407 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel, 8408 FALSE, FALSE, FALSE)) 8409 { 8410 gui_new_scrollbar_colors(); 8411 must_redraw = CLEAR; 8412 } 8413 # endif 8414 } 8415 # endif 8416 # ifdef FEAT_TERMGUICOLORS 8417 # ifdef FEAT_GUI 8418 else 8419 # endif 8420 { 8421 int idx; 8422 8423 idx = syn_name2id((char_u *)"Normal") - 1; 8424 if (idx >= 0) 8425 { 8426 gui_do_one_color(idx, FALSE, FALSE); 8427 8428 /* If the normal fg or bg color changed a complete redraw is 8429 * required. */ 8430 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg 8431 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg) 8432 { 8433 /* if the GUI color is INVALCOLOR then we use the default cterm 8434 * color */ 8435 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg; 8436 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg; 8437 must_redraw = CLEAR; 8438 } 8439 } 8440 } 8441 # endif 8442 } 8443 #endif 8444 8445 #if defined(FEAT_GUI) || defined(PROTO) 8446 /* 8447 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar". 8448 */ 8449 static int 8450 set_group_colors( 8451 char_u *name, 8452 guicolor_T *fgp, 8453 guicolor_T *bgp, 8454 int do_menu, 8455 int use_norm, 8456 int do_tooltip) 8457 { 8458 int idx; 8459 8460 idx = syn_name2id(name) - 1; 8461 if (idx >= 0) 8462 { 8463 gui_do_one_color(idx, do_menu, do_tooltip); 8464 8465 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR) 8466 *fgp = HL_TABLE()[idx].sg_gui_fg; 8467 else if (use_norm) 8468 *fgp = gui.def_norm_pixel; 8469 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR) 8470 *bgp = HL_TABLE()[idx].sg_gui_bg; 8471 else if (use_norm) 8472 *bgp = gui.def_back_pixel; 8473 return TRUE; 8474 } 8475 return FALSE; 8476 } 8477 8478 /* 8479 * Get the font of the "Normal" group. 8480 * Returns "" when it's not found or not set. 8481 */ 8482 char_u * 8483 hl_get_font_name(void) 8484 { 8485 int id; 8486 char_u *s; 8487 8488 id = syn_name2id((char_u *)"Normal"); 8489 if (id > 0) 8490 { 8491 s = HL_TABLE()[id - 1].sg_font_name; 8492 if (s != NULL) 8493 return s; 8494 } 8495 return (char_u *)""; 8496 } 8497 8498 /* 8499 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has 8500 * actually chosen to be used. 8501 */ 8502 void 8503 hl_set_font_name(char_u *font_name) 8504 { 8505 int id; 8506 8507 id = syn_name2id((char_u *)"Normal"); 8508 if (id > 0) 8509 { 8510 vim_free(HL_TABLE()[id - 1].sg_font_name); 8511 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name); 8512 } 8513 } 8514 8515 /* 8516 * Set background color for "Normal" group. Called by gui_set_bg_color() 8517 * when the color is known. 8518 */ 8519 void 8520 hl_set_bg_color_name( 8521 char_u *name) /* must have been allocated */ 8522 { 8523 int id; 8524 8525 if (name != NULL) 8526 { 8527 id = syn_name2id((char_u *)"Normal"); 8528 if (id > 0) 8529 { 8530 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name); 8531 HL_TABLE()[id - 1].sg_gui_bg_name = name; 8532 } 8533 } 8534 } 8535 8536 /* 8537 * Set foreground color for "Normal" group. Called by gui_set_fg_color() 8538 * when the color is known. 8539 */ 8540 void 8541 hl_set_fg_color_name( 8542 char_u *name) /* must have been allocated */ 8543 { 8544 int id; 8545 8546 if (name != NULL) 8547 { 8548 id = syn_name2id((char_u *)"Normal"); 8549 if (id > 0) 8550 { 8551 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name); 8552 HL_TABLE()[id - 1].sg_gui_fg_name = name; 8553 } 8554 } 8555 } 8556 8557 /* 8558 * Return the handle for a font name. 8559 * Returns NOFONT when failed. 8560 */ 8561 static GuiFont 8562 font_name2handle(char_u *name) 8563 { 8564 if (STRCMP(name, "NONE") == 0) 8565 return NOFONT; 8566 8567 return gui_mch_get_font(name, TRUE); 8568 } 8569 8570 # ifdef FEAT_XFONTSET 8571 /* 8572 * Return the handle for a fontset name. 8573 * Returns NOFONTSET when failed. 8574 */ 8575 static GuiFontset 8576 fontset_name2handle(char_u *name, int fixed_width) 8577 { 8578 if (STRCMP(name, "NONE") == 0) 8579 return NOFONTSET; 8580 8581 return gui_mch_get_fontset(name, TRUE, fixed_width); 8582 } 8583 # endif 8584 8585 /* 8586 * Get the font or fontset for one highlight group. 8587 */ 8588 static void 8589 hl_do_font( 8590 int idx, 8591 char_u *arg, 8592 int do_normal, /* set normal font */ 8593 int do_menu UNUSED, /* set menu font */ 8594 int do_tooltip UNUSED, /* set tooltip font */ 8595 int free_font) /* free current font/fontset */ 8596 { 8597 # ifdef FEAT_XFONTSET 8598 /* If 'guifontset' is not empty, first try using the name as a 8599 * fontset. If that doesn't work, use it as a font name. */ 8600 if (*p_guifontset != NUL 8601 # ifdef FONTSET_ALWAYS 8602 || do_menu 8603 # endif 8604 # ifdef FEAT_BEVAL_TIP 8605 /* In Athena & Motif, the Tooltip highlight group is always a fontset */ 8606 || do_tooltip 8607 # endif 8608 ) 8609 { 8610 if (free_font) 8611 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset); 8612 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0 8613 # ifdef FONTSET_ALWAYS 8614 || do_menu 8615 # endif 8616 # ifdef FEAT_BEVAL_TIP 8617 || do_tooltip 8618 # endif 8619 ); 8620 } 8621 if (HL_TABLE()[idx].sg_fontset != NOFONTSET) 8622 { 8623 /* If it worked and it's the Normal group, use it as the normal 8624 * fontset. Same for the Menu group. */ 8625 if (do_normal) 8626 gui_init_font(arg, TRUE); 8627 # if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU) 8628 if (do_menu) 8629 { 8630 # ifdef FONTSET_ALWAYS 8631 gui.menu_fontset = HL_TABLE()[idx].sg_fontset; 8632 # else 8633 /* YIKES! This is a bug waiting to crash the program */ 8634 gui.menu_font = HL_TABLE()[idx].sg_fontset; 8635 # endif 8636 gui_mch_new_menu_font(); 8637 } 8638 # ifdef FEAT_BEVAL_GUI 8639 if (do_tooltip) 8640 { 8641 /* The Athena widget set cannot currently handle switching between 8642 * displaying a single font and a fontset. 8643 * If the XtNinternational resource is set to True at widget 8644 * creation, then a fontset is always used, otherwise an 8645 * XFontStruct is used. 8646 */ 8647 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset; 8648 gui_mch_new_tooltip_font(); 8649 } 8650 # endif 8651 # endif 8652 } 8653 else 8654 # endif 8655 { 8656 if (free_font) 8657 gui_mch_free_font(HL_TABLE()[idx].sg_font); 8658 HL_TABLE()[idx].sg_font = font_name2handle(arg); 8659 /* If it worked and it's the Normal group, use it as the 8660 * normal font. Same for the Menu group. */ 8661 if (HL_TABLE()[idx].sg_font != NOFONT) 8662 { 8663 if (do_normal) 8664 gui_init_font(arg, FALSE); 8665 #ifndef FONTSET_ALWAYS 8666 # if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU) 8667 if (do_menu) 8668 { 8669 gui.menu_font = HL_TABLE()[idx].sg_font; 8670 gui_mch_new_menu_font(); 8671 } 8672 # endif 8673 #endif 8674 } 8675 } 8676 } 8677 8678 #endif /* FEAT_GUI */ 8679 8680 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO) 8681 /* 8682 * Return the handle for a color name. 8683 * Returns INVALCOLOR when failed. 8684 */ 8685 guicolor_T 8686 color_name2handle(char_u *name) 8687 { 8688 if (STRCMP(name, "NONE") == 0) 8689 return INVALCOLOR; 8690 8691 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0) 8692 { 8693 #if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI) 8694 if (gui.in_use) 8695 #endif 8696 #ifdef FEAT_GUI 8697 return gui.norm_pixel; 8698 #endif 8699 #ifdef FEAT_TERMGUICOLORS 8700 if (cterm_normal_fg_gui_color != INVALCOLOR) 8701 return cterm_normal_fg_gui_color; 8702 /* Guess that the foreground is black or white. */ 8703 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white")); 8704 #endif 8705 } 8706 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0) 8707 { 8708 #if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI) 8709 if (gui.in_use) 8710 #endif 8711 #ifdef FEAT_GUI 8712 return gui.back_pixel; 8713 #endif 8714 #ifdef FEAT_TERMGUICOLORS 8715 if (cterm_normal_bg_gui_color != INVALCOLOR) 8716 return cterm_normal_bg_gui_color; 8717 /* Guess that the background is white or black. */ 8718 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black")); 8719 #endif 8720 } 8721 8722 return GUI_GET_COLOR(name); 8723 } 8724 #endif 8725 8726 /* 8727 * Table with the specifications for an attribute number. 8728 * Note that this table is used by ALL buffers. This is required because the 8729 * GUI can redraw at any time for any buffer. 8730 */ 8731 static garray_T term_attr_table = {0, 0, 0, 0, NULL}; 8732 8733 #define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx] 8734 8735 static garray_T cterm_attr_table = {0, 0, 0, 0, NULL}; 8736 8737 #define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx] 8738 8739 #ifdef FEAT_GUI 8740 static garray_T gui_attr_table = {0, 0, 0, 0, NULL}; 8741 8742 #define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx] 8743 #endif 8744 8745 /* 8746 * Return the attr number for a set of colors and font. 8747 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table 8748 * if the combination is new. 8749 * Return 0 for error (no more room). 8750 */ 8751 static int 8752 get_attr_entry(garray_T *table, attrentry_T *aep) 8753 { 8754 int i; 8755 attrentry_T *taep; 8756 static int recursive = FALSE; 8757 8758 /* 8759 * Init the table, in case it wasn't done yet. 8760 */ 8761 table->ga_itemsize = sizeof(attrentry_T); 8762 table->ga_growsize = 7; 8763 8764 /* 8765 * Try to find an entry with the same specifications. 8766 */ 8767 for (i = 0; i < table->ga_len; ++i) 8768 { 8769 taep = &(((attrentry_T *)table->ga_data)[i]); 8770 if ( aep->ae_attr == taep->ae_attr 8771 && ( 8772 #ifdef FEAT_GUI 8773 (table == &gui_attr_table 8774 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color 8775 && aep->ae_u.gui.bg_color 8776 == taep->ae_u.gui.bg_color 8777 && aep->ae_u.gui.sp_color 8778 == taep->ae_u.gui.sp_color 8779 && aep->ae_u.gui.font == taep->ae_u.gui.font 8780 # ifdef FEAT_XFONTSET 8781 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset 8782 # endif 8783 )) 8784 || 8785 #endif 8786 (table == &term_attr_table 8787 && (aep->ae_u.term.start == NULL) 8788 == (taep->ae_u.term.start == NULL) 8789 && (aep->ae_u.term.start == NULL 8790 || STRCMP(aep->ae_u.term.start, 8791 taep->ae_u.term.start) == 0) 8792 && (aep->ae_u.term.stop == NULL) 8793 == (taep->ae_u.term.stop == NULL) 8794 && (aep->ae_u.term.stop == NULL 8795 || STRCMP(aep->ae_u.term.stop, 8796 taep->ae_u.term.stop) == 0)) 8797 || (table == &cterm_attr_table 8798 && aep->ae_u.cterm.fg_color 8799 == taep->ae_u.cterm.fg_color 8800 && aep->ae_u.cterm.bg_color 8801 == taep->ae_u.cterm.bg_color 8802 #ifdef FEAT_TERMGUICOLORS 8803 && aep->ae_u.cterm.fg_rgb 8804 == taep->ae_u.cterm.fg_rgb 8805 && aep->ae_u.cterm.bg_rgb 8806 == taep->ae_u.cterm.bg_rgb 8807 #endif 8808 ))) 8809 8810 return i + ATTR_OFF; 8811 } 8812 8813 if (table->ga_len + ATTR_OFF > MAX_TYPENR) 8814 { 8815 /* 8816 * Running out of attribute entries! remove all attributes, and 8817 * compute new ones for all groups. 8818 * When called recursively, we are really out of numbers. 8819 */ 8820 if (recursive) 8821 { 8822 emsg(_("E424: Too many different highlighting attributes in use")); 8823 return 0; 8824 } 8825 recursive = TRUE; 8826 8827 clear_hl_tables(); 8828 8829 must_redraw = CLEAR; 8830 8831 for (i = 0; i < highlight_ga.ga_len; ++i) 8832 set_hl_attr(i); 8833 8834 recursive = FALSE; 8835 } 8836 8837 /* 8838 * This is a new combination of colors and font, add an entry. 8839 */ 8840 if (ga_grow(table, 1) == FAIL) 8841 return 0; 8842 8843 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]); 8844 vim_memset(taep, 0, sizeof(attrentry_T)); 8845 taep->ae_attr = aep->ae_attr; 8846 #ifdef FEAT_GUI 8847 if (table == &gui_attr_table) 8848 { 8849 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color; 8850 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color; 8851 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color; 8852 taep->ae_u.gui.font = aep->ae_u.gui.font; 8853 # ifdef FEAT_XFONTSET 8854 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset; 8855 # endif 8856 } 8857 #endif 8858 if (table == &term_attr_table) 8859 { 8860 if (aep->ae_u.term.start == NULL) 8861 taep->ae_u.term.start = NULL; 8862 else 8863 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start); 8864 if (aep->ae_u.term.stop == NULL) 8865 taep->ae_u.term.stop = NULL; 8866 else 8867 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop); 8868 } 8869 else if (table == &cterm_attr_table) 8870 { 8871 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color; 8872 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color; 8873 #ifdef FEAT_TERMGUICOLORS 8874 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb; 8875 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb; 8876 #endif 8877 } 8878 ++table->ga_len; 8879 return (table->ga_len - 1 + ATTR_OFF); 8880 } 8881 8882 #if defined(FEAT_TERMINAL) || defined(PROTO) 8883 /* 8884 * Get an attribute index for a cterm entry. 8885 * Uses an existing entry when possible or adds one when needed. 8886 */ 8887 int 8888 get_cterm_attr_idx(int attr, int fg, int bg) 8889 { 8890 attrentry_T at_en; 8891 8892 vim_memset(&at_en, 0, sizeof(attrentry_T)); 8893 #ifdef FEAT_TERMGUICOLORS 8894 at_en.ae_u.cterm.fg_rgb = INVALCOLOR; 8895 at_en.ae_u.cterm.bg_rgb = INVALCOLOR; 8896 #endif 8897 at_en.ae_attr = attr; 8898 at_en.ae_u.cterm.fg_color = fg; 8899 at_en.ae_u.cterm.bg_color = bg; 8900 return get_attr_entry(&cterm_attr_table, &at_en); 8901 } 8902 #endif 8903 8904 #if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO) 8905 /* 8906 * Get an attribute index for a 'termguicolors' entry. 8907 * Uses an existing entry when possible or adds one when needed. 8908 */ 8909 int 8910 get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg) 8911 { 8912 attrentry_T at_en; 8913 8914 vim_memset(&at_en, 0, sizeof(attrentry_T)); 8915 at_en.ae_attr = attr; 8916 if (fg == INVALCOLOR && bg == INVALCOLOR) 8917 { 8918 /* If both GUI colors are not set fall back to the cterm colors. Helps 8919 * if the GUI only has an attribute, such as undercurl. */ 8920 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR; 8921 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR; 8922 } 8923 else 8924 { 8925 at_en.ae_u.cterm.fg_rgb = fg; 8926 at_en.ae_u.cterm.bg_rgb = bg; 8927 } 8928 return get_attr_entry(&cterm_attr_table, &at_en); 8929 } 8930 #endif 8931 8932 #if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO) 8933 /* 8934 * Get an attribute index for a cterm entry. 8935 * Uses an existing entry when possible or adds one when needed. 8936 */ 8937 int 8938 get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg) 8939 { 8940 attrentry_T at_en; 8941 8942 vim_memset(&at_en, 0, sizeof(attrentry_T)); 8943 at_en.ae_attr = attr; 8944 at_en.ae_u.gui.fg_color = fg; 8945 at_en.ae_u.gui.bg_color = bg; 8946 return get_attr_entry(&gui_attr_table, &at_en); 8947 } 8948 #endif 8949 8950 /* 8951 * Clear all highlight tables. 8952 */ 8953 void 8954 clear_hl_tables(void) 8955 { 8956 int i; 8957 attrentry_T *taep; 8958 8959 #ifdef FEAT_GUI 8960 ga_clear(&gui_attr_table); 8961 #endif 8962 for (i = 0; i < term_attr_table.ga_len; ++i) 8963 { 8964 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]); 8965 vim_free(taep->ae_u.term.start); 8966 vim_free(taep->ae_u.term.stop); 8967 } 8968 ga_clear(&term_attr_table); 8969 ga_clear(&cterm_attr_table); 8970 } 8971 8972 #if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO) 8973 /* 8974 * Combine special attributes (e.g., for spelling) with other attributes 8975 * (e.g., for syntax highlighting). 8976 * "prim_attr" overrules "char_attr". 8977 * This creates a new group when required. 8978 * Since we expect there to be few spelling mistakes we don't cache the 8979 * result. 8980 * Return the resulting attributes. 8981 */ 8982 int 8983 hl_combine_attr(int char_attr, int prim_attr) 8984 { 8985 attrentry_T *char_aep = NULL; 8986 attrentry_T *spell_aep; 8987 attrentry_T new_en; 8988 8989 if (char_attr == 0) 8990 return prim_attr; 8991 if (char_attr <= HL_ALL && prim_attr <= HL_ALL) 8992 return ATTR_COMBINE(char_attr, prim_attr); 8993 #ifdef FEAT_GUI 8994 if (gui.in_use) 8995 { 8996 if (char_attr > HL_ALL) 8997 char_aep = syn_gui_attr2entry(char_attr); 8998 if (char_aep != NULL) 8999 new_en = *char_aep; 9000 else 9001 { 9002 vim_memset(&new_en, 0, sizeof(new_en)); 9003 new_en.ae_u.gui.fg_color = INVALCOLOR; 9004 new_en.ae_u.gui.bg_color = INVALCOLOR; 9005 new_en.ae_u.gui.sp_color = INVALCOLOR; 9006 if (char_attr <= HL_ALL) 9007 new_en.ae_attr = char_attr; 9008 } 9009 9010 if (prim_attr <= HL_ALL) 9011 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr); 9012 else 9013 { 9014 spell_aep = syn_gui_attr2entry(prim_attr); 9015 if (spell_aep != NULL) 9016 { 9017 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, 9018 spell_aep->ae_attr); 9019 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR) 9020 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color; 9021 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR) 9022 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color; 9023 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR) 9024 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color; 9025 if (spell_aep->ae_u.gui.font != NOFONT) 9026 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font; 9027 # ifdef FEAT_XFONTSET 9028 if (spell_aep->ae_u.gui.fontset != NOFONTSET) 9029 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset; 9030 # endif 9031 } 9032 } 9033 return get_attr_entry(&gui_attr_table, &new_en); 9034 } 9035 #endif 9036 9037 if (IS_CTERM) 9038 { 9039 if (char_attr > HL_ALL) 9040 char_aep = syn_cterm_attr2entry(char_attr); 9041 if (char_aep != NULL) 9042 new_en = *char_aep; 9043 else 9044 { 9045 vim_memset(&new_en, 0, sizeof(new_en)); 9046 #ifdef FEAT_TERMGUICOLORS 9047 new_en.ae_u.cterm.bg_rgb = INVALCOLOR; 9048 new_en.ae_u.cterm.fg_rgb = INVALCOLOR; 9049 #endif 9050 if (char_attr <= HL_ALL) 9051 new_en.ae_attr = char_attr; 9052 } 9053 9054 if (prim_attr <= HL_ALL) 9055 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr); 9056 else 9057 { 9058 spell_aep = syn_cterm_attr2entry(prim_attr); 9059 if (spell_aep != NULL) 9060 { 9061 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, 9062 spell_aep->ae_attr); 9063 if (spell_aep->ae_u.cterm.fg_color > 0) 9064 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color; 9065 if (spell_aep->ae_u.cterm.bg_color > 0) 9066 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color; 9067 #ifdef FEAT_TERMGUICOLORS 9068 /* If both fg and bg are not set fall back to cterm colors. 9069 * Helps for SpellBad which uses undercurl in the GUI. */ 9070 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb) 9071 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb)) 9072 { 9073 if (spell_aep->ae_u.cterm.fg_color > 0) 9074 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR; 9075 if (spell_aep->ae_u.cterm.bg_color > 0) 9076 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR; 9077 } 9078 else 9079 { 9080 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR) 9081 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb; 9082 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR) 9083 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb; 9084 } 9085 #endif 9086 } 9087 } 9088 return get_attr_entry(&cterm_attr_table, &new_en); 9089 } 9090 9091 if (char_attr > HL_ALL) 9092 char_aep = syn_term_attr2entry(char_attr); 9093 if (char_aep != NULL) 9094 new_en = *char_aep; 9095 else 9096 { 9097 vim_memset(&new_en, 0, sizeof(new_en)); 9098 if (char_attr <= HL_ALL) 9099 new_en.ae_attr = char_attr; 9100 } 9101 9102 if (prim_attr <= HL_ALL) 9103 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr); 9104 else 9105 { 9106 spell_aep = syn_term_attr2entry(prim_attr); 9107 if (spell_aep != NULL) 9108 { 9109 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr); 9110 if (spell_aep->ae_u.term.start != NULL) 9111 { 9112 new_en.ae_u.term.start = spell_aep->ae_u.term.start; 9113 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop; 9114 } 9115 } 9116 } 9117 return get_attr_entry(&term_attr_table, &new_en); 9118 } 9119 #endif 9120 9121 #ifdef FEAT_GUI 9122 9123 attrentry_T * 9124 syn_gui_attr2entry(int attr) 9125 { 9126 attr -= ATTR_OFF; 9127 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */ 9128 return NULL; 9129 return &(GUI_ATTR_ENTRY(attr)); 9130 } 9131 #endif /* FEAT_GUI */ 9132 9133 /* 9134 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr. 9135 * Only to be used when "attr" > HL_ALL. 9136 */ 9137 int 9138 syn_attr2attr(int attr) 9139 { 9140 attrentry_T *aep; 9141 9142 #ifdef FEAT_GUI 9143 if (gui.in_use) 9144 aep = syn_gui_attr2entry(attr); 9145 else 9146 #endif 9147 if (IS_CTERM) 9148 aep = syn_cterm_attr2entry(attr); 9149 else 9150 aep = syn_term_attr2entry(attr); 9151 9152 if (aep == NULL) /* highlighting not set */ 9153 return 0; 9154 return aep->ae_attr; 9155 } 9156 9157 9158 attrentry_T * 9159 syn_term_attr2entry(int attr) 9160 { 9161 attr -= ATTR_OFF; 9162 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */ 9163 return NULL; 9164 return &(TERM_ATTR_ENTRY(attr)); 9165 } 9166 9167 attrentry_T * 9168 syn_cterm_attr2entry(int attr) 9169 { 9170 attr -= ATTR_OFF; 9171 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */ 9172 return NULL; 9173 return &(CTERM_ATTR_ENTRY(attr)); 9174 } 9175 9176 #define LIST_ATTR 1 9177 #define LIST_STRING 2 9178 #define LIST_INT 3 9179 9180 static void 9181 highlight_list_one(int id) 9182 { 9183 struct hl_group *sgp; 9184 int didh = FALSE; 9185 9186 sgp = &HL_TABLE()[id - 1]; // index is ID minus one 9187 9188 if (message_filtered(sgp->sg_name)) 9189 return; 9190 9191 didh = highlight_list_arg(id, didh, LIST_ATTR, 9192 sgp->sg_term, NULL, "term"); 9193 didh = highlight_list_arg(id, didh, LIST_STRING, 9194 0, sgp->sg_start, "start"); 9195 didh = highlight_list_arg(id, didh, LIST_STRING, 9196 0, sgp->sg_stop, "stop"); 9197 9198 didh = highlight_list_arg(id, didh, LIST_ATTR, 9199 sgp->sg_cterm, NULL, "cterm"); 9200 didh = highlight_list_arg(id, didh, LIST_INT, 9201 sgp->sg_cterm_fg, NULL, "ctermfg"); 9202 didh = highlight_list_arg(id, didh, LIST_INT, 9203 sgp->sg_cterm_bg, NULL, "ctermbg"); 9204 9205 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 9206 didh = highlight_list_arg(id, didh, LIST_ATTR, 9207 sgp->sg_gui, NULL, "gui"); 9208 didh = highlight_list_arg(id, didh, LIST_STRING, 9209 0, sgp->sg_gui_fg_name, "guifg"); 9210 didh = highlight_list_arg(id, didh, LIST_STRING, 9211 0, sgp->sg_gui_bg_name, "guibg"); 9212 didh = highlight_list_arg(id, didh, LIST_STRING, 9213 0, sgp->sg_gui_sp_name, "guisp"); 9214 #endif 9215 #ifdef FEAT_GUI 9216 didh = highlight_list_arg(id, didh, LIST_STRING, 9217 0, sgp->sg_font_name, "font"); 9218 #endif 9219 9220 if (sgp->sg_link && !got_int) 9221 { 9222 (void)syn_list_header(didh, 9999, id); 9223 didh = TRUE; 9224 msg_puts_attr("links to", HL_ATTR(HLF_D)); 9225 msg_putchar(' '); 9226 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name); 9227 } 9228 9229 if (!didh) 9230 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", ""); 9231 #ifdef FEAT_EVAL 9232 if (p_verbose > 0) 9233 last_set_msg(sgp->sg_script_ctx); 9234 #endif 9235 } 9236 9237 static int 9238 highlight_list_arg( 9239 int id, 9240 int didh, 9241 int type, 9242 int iarg, 9243 char_u *sarg, 9244 char *name) 9245 { 9246 char_u buf[100]; 9247 char_u *ts; 9248 int i; 9249 9250 if (got_int) 9251 return FALSE; 9252 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0)) 9253 { 9254 ts = buf; 9255 if (type == LIST_INT) 9256 sprintf((char *)buf, "%d", iarg - 1); 9257 else if (type == LIST_STRING) 9258 ts = sarg; 9259 else /* type == LIST_ATTR */ 9260 { 9261 buf[0] = NUL; 9262 for (i = 0; hl_attr_table[i] != 0; ++i) 9263 { 9264 if (iarg & hl_attr_table[i]) 9265 { 9266 if (buf[0] != NUL) 9267 vim_strcat(buf, (char_u *)",", 100); 9268 vim_strcat(buf, (char_u *)hl_name_table[i], 100); 9269 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */ 9270 } 9271 } 9272 } 9273 9274 (void)syn_list_header(didh, 9275 (int)(vim_strsize(ts) + STRLEN(name) + 1), id); 9276 didh = TRUE; 9277 if (!got_int) 9278 { 9279 if (*name != NUL) 9280 { 9281 msg_puts_attr(name, HL_ATTR(HLF_D)); 9282 msg_puts_attr("=", HL_ATTR(HLF_D)); 9283 } 9284 msg_outtrans(ts); 9285 } 9286 } 9287 return didh; 9288 } 9289 9290 #if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO) 9291 /* 9292 * Return "1" if highlight group "id" has attribute "flag". 9293 * Return NULL otherwise. 9294 */ 9295 char_u * 9296 highlight_has_attr( 9297 int id, 9298 int flag, 9299 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */ 9300 { 9301 int attr; 9302 9303 if (id <= 0 || id > highlight_ga.ga_len) 9304 return NULL; 9305 9306 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 9307 if (modec == 'g') 9308 attr = HL_TABLE()[id - 1].sg_gui; 9309 else 9310 #endif 9311 if (modec == 'c') 9312 attr = HL_TABLE()[id - 1].sg_cterm; 9313 else 9314 attr = HL_TABLE()[id - 1].sg_term; 9315 9316 if (attr & flag) 9317 return (char_u *)"1"; 9318 return NULL; 9319 } 9320 #endif 9321 9322 #if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO) 9323 /* 9324 * Return color name of highlight group "id". 9325 */ 9326 char_u * 9327 highlight_color( 9328 int id, 9329 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */ 9330 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */ 9331 { 9332 static char_u name[20]; 9333 int n; 9334 int fg = FALSE; 9335 int sp = FALSE; 9336 int font = FALSE; 9337 9338 if (id <= 0 || id > highlight_ga.ga_len) 9339 return NULL; 9340 9341 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g') 9342 fg = TRUE; 9343 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o' 9344 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't') 9345 font = TRUE; 9346 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p') 9347 sp = TRUE; 9348 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) 9349 return NULL; 9350 if (modec == 'g') 9351 { 9352 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 9353 # ifdef FEAT_GUI 9354 /* return font name */ 9355 if (font) 9356 return HL_TABLE()[id - 1].sg_font_name; 9357 # endif 9358 9359 /* return #RRGGBB form (only possible when GUI is running) */ 9360 if ((USE_24BIT) && what[2] == '#') 9361 { 9362 guicolor_T color; 9363 long_u rgb; 9364 static char_u buf[10]; 9365 9366 if (fg) 9367 color = HL_TABLE()[id - 1].sg_gui_fg; 9368 else if (sp) 9369 # ifdef FEAT_GUI 9370 color = HL_TABLE()[id - 1].sg_gui_sp; 9371 # else 9372 color = INVALCOLOR; 9373 # endif 9374 else 9375 color = HL_TABLE()[id - 1].sg_gui_bg; 9376 if (color == INVALCOLOR) 9377 return NULL; 9378 rgb = (long_u)GUI_MCH_GET_RGB(color); 9379 sprintf((char *)buf, "#%02x%02x%02x", 9380 (unsigned)(rgb >> 16), 9381 (unsigned)(rgb >> 8) & 255, 9382 (unsigned)rgb & 255); 9383 return buf; 9384 } 9385 # endif 9386 if (fg) 9387 return (HL_TABLE()[id - 1].sg_gui_fg_name); 9388 if (sp) 9389 return (HL_TABLE()[id - 1].sg_gui_sp_name); 9390 return (HL_TABLE()[id - 1].sg_gui_bg_name); 9391 } 9392 if (font || sp) 9393 return NULL; 9394 if (modec == 'c') 9395 { 9396 if (fg) 9397 n = HL_TABLE()[id - 1].sg_cterm_fg - 1; 9398 else 9399 n = HL_TABLE()[id - 1].sg_cterm_bg - 1; 9400 if (n < 0) 9401 return NULL; 9402 sprintf((char *)name, "%d", n); 9403 return name; 9404 } 9405 /* term doesn't have color */ 9406 return NULL; 9407 } 9408 #endif 9409 9410 #if (defined(FEAT_SYN_HL) \ 9411 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \ 9412 && defined(FEAT_PRINTER)) || defined(PROTO) 9413 /* 9414 * Return color name of highlight group "id" as RGB value. 9415 */ 9416 long_u 9417 highlight_gui_color_rgb( 9418 int id, 9419 int fg) /* TRUE = fg, FALSE = bg */ 9420 { 9421 guicolor_T color; 9422 9423 if (id <= 0 || id > highlight_ga.ga_len) 9424 return 0L; 9425 9426 if (fg) 9427 color = HL_TABLE()[id - 1].sg_gui_fg; 9428 else 9429 color = HL_TABLE()[id - 1].sg_gui_bg; 9430 9431 if (color == INVALCOLOR) 9432 return 0L; 9433 9434 return GUI_MCH_GET_RGB(color); 9435 } 9436 #endif 9437 9438 /* 9439 * Output the syntax list header. 9440 * Return TRUE when started a new line. 9441 */ 9442 static int 9443 syn_list_header( 9444 int did_header, /* did header already */ 9445 int outlen, /* length of string that comes */ 9446 int id) /* highlight group id */ 9447 { 9448 int endcol = 19; 9449 int newline = TRUE; 9450 9451 if (!did_header) 9452 { 9453 msg_putchar('\n'); 9454 if (got_int) 9455 return TRUE; 9456 msg_outtrans(HL_TABLE()[id - 1].sg_name); 9457 endcol = 15; 9458 } 9459 else if (msg_col + outlen + 1 >= Columns) 9460 { 9461 msg_putchar('\n'); 9462 if (got_int) 9463 return TRUE; 9464 } 9465 else 9466 { 9467 if (msg_col >= endcol) /* wrap around is like starting a new line */ 9468 newline = FALSE; 9469 } 9470 9471 if (msg_col >= endcol) /* output at least one space */ 9472 endcol = msg_col + 1; 9473 if (Columns <= endcol) /* avoid hang for tiny window */ 9474 endcol = Columns - 1; 9475 9476 msg_advance(endcol); 9477 9478 /* Show "xxx" with the attributes. */ 9479 if (!did_header) 9480 { 9481 msg_puts_attr("xxx", syn_id2attr(id)); 9482 msg_putchar(' '); 9483 } 9484 9485 return newline; 9486 } 9487 9488 /* 9489 * Set the attribute numbers for a highlight group. 9490 * Called after one of the attributes has changed. 9491 */ 9492 static void 9493 set_hl_attr( 9494 int idx) /* index in array */ 9495 { 9496 attrentry_T at_en; 9497 struct hl_group *sgp = HL_TABLE() + idx; 9498 9499 /* The "Normal" group doesn't need an attribute number */ 9500 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0) 9501 return; 9502 9503 #ifdef FEAT_GUI 9504 /* 9505 * For the GUI mode: If there are other than "normal" highlighting 9506 * attributes, need to allocate an attr number. 9507 */ 9508 if (sgp->sg_gui_fg == INVALCOLOR 9509 && sgp->sg_gui_bg == INVALCOLOR 9510 && sgp->sg_gui_sp == INVALCOLOR 9511 && sgp->sg_font == NOFONT 9512 # ifdef FEAT_XFONTSET 9513 && sgp->sg_fontset == NOFONTSET 9514 # endif 9515 ) 9516 { 9517 sgp->sg_gui_attr = sgp->sg_gui; 9518 } 9519 else 9520 { 9521 at_en.ae_attr = sgp->sg_gui; 9522 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg; 9523 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg; 9524 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp; 9525 at_en.ae_u.gui.font = sgp->sg_font; 9526 # ifdef FEAT_XFONTSET 9527 at_en.ae_u.gui.fontset = sgp->sg_fontset; 9528 # endif 9529 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en); 9530 } 9531 #endif 9532 /* 9533 * For the term mode: If there are other than "normal" highlighting 9534 * attributes, need to allocate an attr number. 9535 */ 9536 if (sgp->sg_start == NULL && sgp->sg_stop == NULL) 9537 sgp->sg_term_attr = sgp->sg_term; 9538 else 9539 { 9540 at_en.ae_attr = sgp->sg_term; 9541 at_en.ae_u.term.start = sgp->sg_start; 9542 at_en.ae_u.term.stop = sgp->sg_stop; 9543 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en); 9544 } 9545 9546 /* 9547 * For the color term mode: If there are other than "normal" 9548 * highlighting attributes, need to allocate an attr number. 9549 */ 9550 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 9551 # ifdef FEAT_TERMGUICOLORS 9552 && sgp->sg_gui_fg == INVALCOLOR 9553 && sgp->sg_gui_bg == INVALCOLOR 9554 # endif 9555 ) 9556 sgp->sg_cterm_attr = sgp->sg_cterm; 9557 else 9558 { 9559 at_en.ae_attr = sgp->sg_cterm; 9560 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg; 9561 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg; 9562 # ifdef FEAT_TERMGUICOLORS 9563 # ifdef MSWIN 9564 { 9565 int id; 9566 guicolor_T fg, bg; 9567 9568 id = syn_name2id((char_u *)"Normal"); 9569 if (id > 0) 9570 { 9571 syn_id2colors(id, &fg, &bg); 9572 if (sgp->sg_gui_fg == INVALCOLOR) 9573 sgp->sg_gui_fg = fg; 9574 if (sgp->sg_gui_bg == INVALCOLOR) 9575 sgp->sg_gui_bg = bg; 9576 } 9577 9578 } 9579 # endif 9580 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg); 9581 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg); 9582 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR 9583 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR) 9584 { 9585 /* If both fg and bg are invalid fall back to the cterm colors. 9586 * Helps when the GUI only uses an attribute, e.g. undercurl. */ 9587 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR; 9588 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR; 9589 } 9590 # endif 9591 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en); 9592 } 9593 } 9594 9595 /* 9596 * Lookup a highlight group name and return its ID. 9597 * If it is not found, 0 is returned. 9598 */ 9599 int 9600 syn_name2id(char_u *name) 9601 { 9602 int i; 9603 char_u name_u[200]; 9604 9605 /* Avoid using stricmp() too much, it's slow on some systems */ 9606 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars 9607 * don't deserve to be found! */ 9608 vim_strncpy(name_u, name, 199); 9609 vim_strup(name_u); 9610 for (i = highlight_ga.ga_len; --i >= 0; ) 9611 if (HL_TABLE()[i].sg_name_u != NULL 9612 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0) 9613 break; 9614 return i + 1; 9615 } 9616 9617 /* 9618 * Lookup a highlight group name and return its attributes. 9619 * Return zero if not found. 9620 */ 9621 int 9622 syn_name2attr(char_u *name) 9623 { 9624 int id = syn_name2id(name); 9625 9626 if (id != 0) 9627 return syn_id2attr(id); 9628 return 0; 9629 } 9630 9631 #if defined(FEAT_EVAL) || defined(PROTO) 9632 /* 9633 * Return TRUE if highlight group "name" exists. 9634 */ 9635 int 9636 highlight_exists(char_u *name) 9637 { 9638 return (syn_name2id(name) > 0); 9639 } 9640 9641 # if defined(FEAT_SEARCH_EXTRA) || defined(PROTO) 9642 /* 9643 * Return the name of highlight group "id". 9644 * When not a valid ID return an empty string. 9645 */ 9646 char_u * 9647 syn_id2name(int id) 9648 { 9649 if (id <= 0 || id > highlight_ga.ga_len) 9650 return (char_u *)""; 9651 return HL_TABLE()[id - 1].sg_name; 9652 } 9653 # endif 9654 #endif 9655 9656 /* 9657 * Like syn_name2id(), but take a pointer + length argument. 9658 */ 9659 int 9660 syn_namen2id(char_u *linep, int len) 9661 { 9662 char_u *name; 9663 int id = 0; 9664 9665 name = vim_strnsave(linep, len); 9666 if (name != NULL) 9667 { 9668 id = syn_name2id(name); 9669 vim_free(name); 9670 } 9671 return id; 9672 } 9673 9674 /* 9675 * Find highlight group name in the table and return its ID. 9676 * The argument is a pointer to the name and the length of the name. 9677 * If it doesn't exist yet, a new entry is created. 9678 * Return 0 for failure. 9679 */ 9680 int 9681 syn_check_group(char_u *pp, int len) 9682 { 9683 int id; 9684 char_u *name; 9685 9686 name = vim_strnsave(pp, len); 9687 if (name == NULL) 9688 return 0; 9689 9690 id = syn_name2id(name); 9691 if (id == 0) /* doesn't exist yet */ 9692 id = syn_add_group(name); 9693 else 9694 vim_free(name); 9695 return id; 9696 } 9697 9698 /* 9699 * Add new highlight group and return its ID. 9700 * "name" must be an allocated string, it will be consumed. 9701 * Return 0 for failure. 9702 */ 9703 static int 9704 syn_add_group(char_u *name) 9705 { 9706 char_u *p; 9707 9708 /* Check that the name is ASCII letters, digits and underscore. */ 9709 for (p = name; *p != NUL; ++p) 9710 { 9711 if (!vim_isprintc(*p)) 9712 { 9713 emsg(_("E669: Unprintable character in group name")); 9714 vim_free(name); 9715 return 0; 9716 } 9717 else if (!ASCII_ISALNUM(*p) && *p != '_') 9718 { 9719 /* This is an error, but since there previously was no check only 9720 * give a warning. */ 9721 msg_source(HL_ATTR(HLF_W)); 9722 msg(_("W18: Invalid character in group name")); 9723 break; 9724 } 9725 } 9726 9727 /* 9728 * First call for this growarray: init growing array. 9729 */ 9730 if (highlight_ga.ga_data == NULL) 9731 { 9732 highlight_ga.ga_itemsize = sizeof(struct hl_group); 9733 highlight_ga.ga_growsize = 10; 9734 } 9735 9736 if (highlight_ga.ga_len >= MAX_HL_ID) 9737 { 9738 emsg(_("E849: Too many highlight and syntax groups")); 9739 vim_free(name); 9740 return 0; 9741 } 9742 9743 /* 9744 * Make room for at least one other syntax_highlight entry. 9745 */ 9746 if (ga_grow(&highlight_ga, 1) == FAIL) 9747 { 9748 vim_free(name); 9749 return 0; 9750 } 9751 9752 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group)); 9753 HL_TABLE()[highlight_ga.ga_len].sg_name = name; 9754 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name); 9755 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 9756 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR; 9757 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR; 9758 # ifdef FEAT_GUI 9759 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR; 9760 # endif 9761 #endif 9762 ++highlight_ga.ga_len; 9763 9764 return highlight_ga.ga_len; /* ID is index plus one */ 9765 } 9766 9767 /* 9768 * When, just after calling syn_add_group(), an error is discovered, this 9769 * function deletes the new name. 9770 */ 9771 static void 9772 syn_unadd_group(void) 9773 { 9774 --highlight_ga.ga_len; 9775 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name); 9776 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u); 9777 } 9778 9779 /* 9780 * Translate a group ID to highlight attributes. 9781 */ 9782 int 9783 syn_id2attr(int hl_id) 9784 { 9785 int attr; 9786 struct hl_group *sgp; 9787 9788 hl_id = syn_get_final_id(hl_id); 9789 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */ 9790 9791 #ifdef FEAT_GUI 9792 /* 9793 * Only use GUI attr when the GUI is being used. 9794 */ 9795 if (gui.in_use) 9796 attr = sgp->sg_gui_attr; 9797 else 9798 #endif 9799 if (IS_CTERM) 9800 attr = sgp->sg_cterm_attr; 9801 else 9802 attr = sgp->sg_term_attr; 9803 9804 return attr; 9805 } 9806 9807 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO) 9808 /* 9809 * Get the GUI colors and attributes for a group ID. 9810 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise. 9811 */ 9812 int 9813 syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp) 9814 { 9815 struct hl_group *sgp; 9816 9817 hl_id = syn_get_final_id(hl_id); 9818 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */ 9819 9820 *fgp = sgp->sg_gui_fg; 9821 *bgp = sgp->sg_gui_bg; 9822 return sgp->sg_gui; 9823 } 9824 #endif 9825 9826 #if (defined(MSWIN) \ 9827 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \ 9828 && defined(FEAT_TERMGUICOLORS)) || defined(PROTO) 9829 void 9830 syn_id2cterm_bg(int hl_id, int *fgp, int *bgp) 9831 { 9832 struct hl_group *sgp; 9833 9834 hl_id = syn_get_final_id(hl_id); 9835 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */ 9836 *fgp = sgp->sg_cterm_fg - 1; 9837 *bgp = sgp->sg_cterm_bg - 1; 9838 } 9839 #endif 9840 9841 /* 9842 * Translate a group ID to the final group ID (following links). 9843 */ 9844 int 9845 syn_get_final_id(int hl_id) 9846 { 9847 int count; 9848 struct hl_group *sgp; 9849 9850 if (hl_id > highlight_ga.ga_len || hl_id < 1) 9851 return 0; /* Can be called from eval!! */ 9852 9853 /* 9854 * Follow links until there is no more. 9855 * Look out for loops! Break after 100 links. 9856 */ 9857 for (count = 100; --count >= 0; ) 9858 { 9859 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */ 9860 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len) 9861 break; 9862 hl_id = sgp->sg_link; 9863 } 9864 9865 return hl_id; 9866 } 9867 9868 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO) 9869 /* 9870 * Call this function just after the GUI has started. 9871 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then. 9872 * It finds the font and color handles for the highlighting groups. 9873 */ 9874 void 9875 highlight_gui_started(void) 9876 { 9877 int idx; 9878 9879 /* First get the colors from the "Normal" and "Menu" group, if set */ 9880 if (USE_24BIT) 9881 set_normal_colors(); 9882 9883 for (idx = 0; idx < highlight_ga.ga_len; ++idx) 9884 gui_do_one_color(idx, FALSE, FALSE); 9885 9886 highlight_changed(); 9887 } 9888 9889 static void 9890 gui_do_one_color( 9891 int idx, 9892 int do_menu UNUSED, /* TRUE: might set the menu font */ 9893 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */ 9894 { 9895 int didit = FALSE; 9896 9897 # ifdef FEAT_GUI 9898 # ifdef FEAT_TERMGUICOLORS 9899 if (gui.in_use) 9900 # endif 9901 if (HL_TABLE()[idx].sg_font_name != NULL) 9902 { 9903 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu, 9904 do_tooltip, TRUE); 9905 didit = TRUE; 9906 } 9907 # endif 9908 if (HL_TABLE()[idx].sg_gui_fg_name != NULL) 9909 { 9910 HL_TABLE()[idx].sg_gui_fg = 9911 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name); 9912 didit = TRUE; 9913 } 9914 if (HL_TABLE()[idx].sg_gui_bg_name != NULL) 9915 { 9916 HL_TABLE()[idx].sg_gui_bg = 9917 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name); 9918 didit = TRUE; 9919 } 9920 # ifdef FEAT_GUI 9921 if (HL_TABLE()[idx].sg_gui_sp_name != NULL) 9922 { 9923 HL_TABLE()[idx].sg_gui_sp = 9924 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name); 9925 didit = TRUE; 9926 } 9927 # endif 9928 if (didit) /* need to get a new attr number */ 9929 set_hl_attr(idx); 9930 } 9931 #endif 9932 9933 #if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT) 9934 /* 9935 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC. 9936 */ 9937 static void 9938 combine_stl_hlt( 9939 int id, 9940 int id_S, 9941 int id_alt, 9942 int hlcnt, 9943 int i, 9944 int hlf, 9945 int *table) 9946 { 9947 struct hl_group *hlt = HL_TABLE(); 9948 9949 if (id_alt == 0) 9950 { 9951 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group)); 9952 hlt[hlcnt + i].sg_term = highlight_attr[hlf]; 9953 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf]; 9954 # if defined(FEAT_GUI) || defined(FEAT_EVAL) 9955 hlt[hlcnt + i].sg_gui = highlight_attr[hlf]; 9956 # endif 9957 } 9958 else 9959 mch_memmove(&hlt[hlcnt + i], 9960 &hlt[id_alt - 1], 9961 sizeof(struct hl_group)); 9962 hlt[hlcnt + i].sg_link = 0; 9963 9964 hlt[hlcnt + i].sg_term ^= 9965 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term; 9966 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start) 9967 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start; 9968 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop) 9969 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop; 9970 hlt[hlcnt + i].sg_cterm ^= 9971 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm; 9972 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg) 9973 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg; 9974 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg) 9975 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg; 9976 # if defined(FEAT_GUI) || defined(FEAT_EVAL) 9977 hlt[hlcnt + i].sg_gui ^= 9978 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui; 9979 # endif 9980 # ifdef FEAT_GUI 9981 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg) 9982 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg; 9983 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg) 9984 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg; 9985 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp) 9986 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp; 9987 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font) 9988 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font; 9989 # ifdef FEAT_XFONTSET 9990 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset) 9991 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset; 9992 # endif 9993 # endif 9994 highlight_ga.ga_len = hlcnt + i + 1; 9995 set_hl_attr(hlcnt + i); /* At long last we can apply */ 9996 table[i] = syn_id2attr(hlcnt + i + 1); 9997 } 9998 #endif 9999 10000 /* 10001 * Translate the 'highlight' option into attributes in highlight_attr[] and 10002 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of 10003 * corresponding highlights to use on top of HLF_SNC is computed. 10004 * Called only when the 'highlight' option has been changed and upon first 10005 * screen redraw after any :highlight command. 10006 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise. 10007 */ 10008 int 10009 highlight_changed(void) 10010 { 10011 int hlf; 10012 int i; 10013 char_u *p; 10014 int attr; 10015 char_u *end; 10016 int id; 10017 #ifdef USER_HIGHLIGHT 10018 char_u userhl[30]; // use 30 to avoid compiler warning 10019 # ifdef FEAT_STL_OPT 10020 int id_S = -1; 10021 int id_SNC = 0; 10022 # ifdef FEAT_TERMINAL 10023 int id_ST = 0; 10024 int id_STNC = 0; 10025 # endif 10026 int hlcnt; 10027 # endif 10028 #endif 10029 static int hl_flags[HLF_COUNT] = HL_FLAGS; 10030 10031 need_highlight_changed = FALSE; 10032 10033 /* 10034 * Clear all attributes. 10035 */ 10036 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf) 10037 highlight_attr[hlf] = 0; 10038 10039 /* 10040 * First set all attributes to their default value. 10041 * Then use the attributes from the 'highlight' option. 10042 */ 10043 for (i = 0; i < 2; ++i) 10044 { 10045 if (i) 10046 p = p_hl; 10047 else 10048 p = get_highlight_default(); 10049 if (p == NULL) /* just in case */ 10050 continue; 10051 10052 while (*p) 10053 { 10054 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf) 10055 if (hl_flags[hlf] == *p) 10056 break; 10057 ++p; 10058 if (hlf == (int)HLF_COUNT || *p == NUL) 10059 return FAIL; 10060 10061 /* 10062 * Allow several hl_flags to be combined, like "bu" for 10063 * bold-underlined. 10064 */ 10065 attr = 0; 10066 for ( ; *p && *p != ','; ++p) /* parse upto comma */ 10067 { 10068 if (VIM_ISWHITE(*p)) /* ignore white space */ 10069 continue; 10070 10071 if (attr > HL_ALL) /* Combination with ':' is not allowed. */ 10072 return FAIL; 10073 10074 switch (*p) 10075 { 10076 case 'b': attr |= HL_BOLD; 10077 break; 10078 case 'i': attr |= HL_ITALIC; 10079 break; 10080 case '-': 10081 case 'n': /* no highlighting */ 10082 break; 10083 case 'r': attr |= HL_INVERSE; 10084 break; 10085 case 's': attr |= HL_STANDOUT; 10086 break; 10087 case 'u': attr |= HL_UNDERLINE; 10088 break; 10089 case 'c': attr |= HL_UNDERCURL; 10090 break; 10091 case 't': attr |= HL_STRIKETHROUGH; 10092 break; 10093 case ':': ++p; /* highlight group name */ 10094 if (attr || *p == NUL) /* no combinations */ 10095 return FAIL; 10096 end = vim_strchr(p, ','); 10097 if (end == NULL) 10098 end = p + STRLEN(p); 10099 id = syn_check_group(p, (int)(end - p)); 10100 if (id == 0) 10101 return FAIL; 10102 attr = syn_id2attr(id); 10103 p = end - 1; 10104 #if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT) 10105 if (hlf == (int)HLF_SNC) 10106 id_SNC = syn_get_final_id(id); 10107 # ifdef FEAT_TERMINAL 10108 else if (hlf == (int)HLF_ST) 10109 id_ST = syn_get_final_id(id); 10110 else if (hlf == (int)HLF_STNC) 10111 id_STNC = syn_get_final_id(id); 10112 # endif 10113 else if (hlf == (int)HLF_S) 10114 id_S = syn_get_final_id(id); 10115 #endif 10116 break; 10117 default: return FAIL; 10118 } 10119 } 10120 highlight_attr[hlf] = attr; 10121 10122 p = skip_to_option_part(p); /* skip comma and spaces */ 10123 } 10124 } 10125 10126 #ifdef USER_HIGHLIGHT 10127 /* Setup the user highlights 10128 * 10129 * Temporarily utilize 28 more hl entries: 10130 * 9 for User1-User9 combined with StatusLineNC 10131 * 9 for User1-User9 combined with StatusLineTerm 10132 * 9 for User1-User9 combined with StatusLineTermNC 10133 * 1 for StatusLine default 10134 * Have to be in there simultaneously in case of table overflows in 10135 * get_attr_entry() 10136 */ 10137 # ifdef FEAT_STL_OPT 10138 if (ga_grow(&highlight_ga, 28) == FAIL) 10139 return FAIL; 10140 hlcnt = highlight_ga.ga_len; 10141 if (id_S == -1) 10142 { 10143 /* Make sure id_S is always valid to simplify code below. Use the last 10144 * entry. */ 10145 vim_memset(&HL_TABLE()[hlcnt + 27], 0, sizeof(struct hl_group)); 10146 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S]; 10147 id_S = hlcnt + 19; 10148 } 10149 # endif 10150 for (i = 0; i < 9; i++) 10151 { 10152 sprintf((char *)userhl, "User%d", i + 1); 10153 id = syn_name2id(userhl); 10154 if (id == 0) 10155 { 10156 highlight_user[i] = 0; 10157 # ifdef FEAT_STL_OPT 10158 highlight_stlnc[i] = 0; 10159 # ifdef FEAT_TERMINAL 10160 highlight_stlterm[i] = 0; 10161 highlight_stltermnc[i] = 0; 10162 # endif 10163 # endif 10164 } 10165 else 10166 { 10167 highlight_user[i] = syn_id2attr(id); 10168 # ifdef FEAT_STL_OPT 10169 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i, 10170 HLF_SNC, highlight_stlnc); 10171 # ifdef FEAT_TERMINAL 10172 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i, 10173 HLF_ST, highlight_stlterm); 10174 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i, 10175 HLF_STNC, highlight_stltermnc); 10176 # endif 10177 # endif 10178 } 10179 } 10180 # ifdef FEAT_STL_OPT 10181 highlight_ga.ga_len = hlcnt; 10182 # endif 10183 10184 #endif /* USER_HIGHLIGHT */ 10185 10186 return OK; 10187 } 10188 10189 #if defined(FEAT_CMDL_COMPL) || defined(PROTO) 10190 10191 static void highlight_list(void); 10192 static void highlight_list_two(int cnt, int attr); 10193 10194 /* 10195 * Handle command line completion for :highlight command. 10196 */ 10197 void 10198 set_context_in_highlight_cmd(expand_T *xp, char_u *arg) 10199 { 10200 char_u *p; 10201 10202 /* Default: expand group names */ 10203 xp->xp_context = EXPAND_HIGHLIGHT; 10204 xp->xp_pattern = arg; 10205 include_link = 2; 10206 include_default = 1; 10207 10208 /* (part of) subcommand already typed */ 10209 if (*arg != NUL) 10210 { 10211 p = skiptowhite(arg); 10212 if (*p != NUL) /* past "default" or group name */ 10213 { 10214 include_default = 0; 10215 if (STRNCMP("default", arg, p - arg) == 0) 10216 { 10217 arg = skipwhite(p); 10218 xp->xp_pattern = arg; 10219 p = skiptowhite(arg); 10220 } 10221 if (*p != NUL) /* past group name */ 10222 { 10223 include_link = 0; 10224 if (arg[1] == 'i' && arg[0] == 'N') 10225 highlight_list(); 10226 if (STRNCMP("link", arg, p - arg) == 0 10227 || STRNCMP("clear", arg, p - arg) == 0) 10228 { 10229 xp->xp_pattern = skipwhite(p); 10230 p = skiptowhite(xp->xp_pattern); 10231 if (*p != NUL) /* past first group name */ 10232 { 10233 xp->xp_pattern = skipwhite(p); 10234 p = skiptowhite(xp->xp_pattern); 10235 } 10236 } 10237 if (*p != NUL) /* past group name(s) */ 10238 xp->xp_context = EXPAND_NOTHING; 10239 } 10240 } 10241 } 10242 } 10243 10244 /* 10245 * List highlighting matches in a nice way. 10246 */ 10247 static void 10248 highlight_list(void) 10249 { 10250 int i; 10251 10252 for (i = 10; --i >= 0; ) 10253 highlight_list_two(i, HL_ATTR(HLF_D)); 10254 for (i = 40; --i >= 0; ) 10255 highlight_list_two(99, 0); 10256 } 10257 10258 static void 10259 highlight_list_two(int cnt, int attr) 10260 { 10261 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr); 10262 msg_clr_eos(); 10263 out_flush(); 10264 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE); 10265 } 10266 10267 #endif /* FEAT_CMDL_COMPL */ 10268 10269 #if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \ 10270 || defined(FEAT_SIGNS) || defined(PROTO) 10271 /* 10272 * Function given to ExpandGeneric() to obtain the list of group names. 10273 */ 10274 char_u * 10275 get_highlight_name(expand_T *xp UNUSED, int idx) 10276 { 10277 return get_highlight_name_ext(xp, idx, TRUE); 10278 } 10279 10280 /* 10281 * Obtain a highlight group name. 10282 * When "skip_cleared" is TRUE don't return a cleared entry. 10283 */ 10284 char_u * 10285 get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared) 10286 { 10287 if (idx < 0) 10288 return NULL; 10289 10290 /* Items are never removed from the table, skip the ones that were 10291 * cleared. */ 10292 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared) 10293 return (char_u *)""; 10294 10295 #ifdef FEAT_CMDL_COMPL 10296 if (idx == highlight_ga.ga_len && include_none != 0) 10297 return (char_u *)"none"; 10298 if (idx == highlight_ga.ga_len + include_none && include_default != 0) 10299 return (char_u *)"default"; 10300 if (idx == highlight_ga.ga_len + include_none + include_default 10301 && include_link != 0) 10302 return (char_u *)"link"; 10303 if (idx == highlight_ga.ga_len + include_none + include_default + 1 10304 && include_link != 0) 10305 return (char_u *)"clear"; 10306 #endif 10307 if (idx >= highlight_ga.ga_len) 10308 return NULL; 10309 return HL_TABLE()[idx].sg_name; 10310 } 10311 #endif 10312 10313 #if defined(FEAT_GUI) || defined(PROTO) 10314 /* 10315 * Free all the highlight group fonts. 10316 * Used when quitting for systems which need it. 10317 */ 10318 void 10319 free_highlight_fonts(void) 10320 { 10321 int idx; 10322 10323 for (idx = 0; idx < highlight_ga.ga_len; ++idx) 10324 { 10325 gui_mch_free_font(HL_TABLE()[idx].sg_font); 10326 HL_TABLE()[idx].sg_font = NOFONT; 10327 # ifdef FEAT_XFONTSET 10328 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset); 10329 HL_TABLE()[idx].sg_fontset = NOFONTSET; 10330 # endif 10331 } 10332 10333 gui_mch_free_font(gui.norm_font); 10334 # ifdef FEAT_XFONTSET 10335 gui_mch_free_fontset(gui.fontset); 10336 # endif 10337 # ifndef FEAT_GUI_GTK 10338 gui_mch_free_font(gui.bold_font); 10339 gui_mch_free_font(gui.ital_font); 10340 gui_mch_free_font(gui.boldital_font); 10341 # endif 10342 } 10343 #endif 10344 10345 /************************************** 10346 * End of Highlighting stuff * 10347 **************************************/ 10348