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