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