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