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