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