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