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