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 #ifdef FEAT_TERMINAL 6891 CENT("StatusLineTerm term=reverse cterm=reverse ctermFg=DarkGreen", 6892 "StatusLineTerm term=reverse cterm=reverse ctermFg=DarkGreen gui=reverse guifg=DarkGreen"), 6893 #endif 6894 "default link EndOfBuffer NonText", 6895 #ifdef FEAT_WINDOWS 6896 CENT("VertSplit term=reverse cterm=reverse", 6897 "VertSplit term=reverse cterm=reverse gui=reverse"), 6898 #endif 6899 #ifdef FEAT_CLIPBOARD 6900 CENT("VisualNOS term=underline,bold cterm=underline,bold", 6901 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"), 6902 #endif 6903 #ifdef FEAT_DIFF 6904 CENT("DiffText term=reverse cterm=bold ctermbg=Red", 6905 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"), 6906 #endif 6907 #ifdef FEAT_INS_EXPAND 6908 CENT("PmenuSbar ctermbg=Grey", 6909 "PmenuSbar ctermbg=Grey guibg=Grey"), 6910 #endif 6911 #ifdef FEAT_WINDOWS 6912 CENT("TabLineSel term=bold cterm=bold", 6913 "TabLineSel term=bold cterm=bold gui=bold"), 6914 CENT("TabLineFill term=reverse cterm=reverse", 6915 "TabLineFill term=reverse cterm=reverse gui=reverse"), 6916 #endif 6917 #ifdef FEAT_GUI 6918 "Cursor guibg=fg guifg=bg", 6919 "lCursor guibg=fg guifg=bg", /* should be different, but what? */ 6920 #endif 6921 "default link QuickFixLine Search", 6922 NULL 6923 }; 6924 6925 static char *(highlight_init_light[]) = 6926 { 6927 CENT("Directory term=bold ctermfg=DarkBlue", 6928 "Directory term=bold ctermfg=DarkBlue guifg=Blue"), 6929 CENT("LineNr term=underline ctermfg=Brown", 6930 "LineNr term=underline ctermfg=Brown guifg=Brown"), 6931 CENT("CursorLineNr term=bold ctermfg=Brown", 6932 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"), 6933 CENT("MoreMsg term=bold ctermfg=DarkGreen", 6934 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"), 6935 CENT("Question term=standout ctermfg=DarkGreen", 6936 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"), 6937 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE", 6938 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"), 6939 #ifdef FEAT_SPELL 6940 CENT("SpellBad term=reverse ctermbg=LightRed", 6941 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"), 6942 CENT("SpellCap term=reverse ctermbg=LightBlue", 6943 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"), 6944 CENT("SpellRare term=reverse ctermbg=LightMagenta", 6945 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"), 6946 CENT("SpellLocal term=underline ctermbg=Cyan", 6947 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"), 6948 #endif 6949 #ifdef FEAT_INS_EXPAND 6950 CENT("PmenuThumb ctermbg=Black", 6951 "PmenuThumb ctermbg=Black guibg=Black"), 6952 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black", 6953 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"), 6954 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black", 6955 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"), 6956 #endif 6957 CENT("SpecialKey term=bold ctermfg=DarkBlue", 6958 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"), 6959 CENT("Title term=bold ctermfg=DarkMagenta", 6960 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"), 6961 CENT("WarningMsg term=standout ctermfg=DarkRed", 6962 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"), 6963 #ifdef FEAT_WILDMENU 6964 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black", 6965 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"), 6966 #endif 6967 #ifdef FEAT_FOLDING 6968 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue", 6969 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"), 6970 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue", 6971 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"), 6972 #endif 6973 #ifdef FEAT_SIGNS 6974 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue", 6975 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"), 6976 #endif 6977 CENT("Visual term=reverse", 6978 "Visual term=reverse guibg=LightGrey"), 6979 #ifdef FEAT_DIFF 6980 CENT("DiffAdd term=bold ctermbg=LightBlue", 6981 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"), 6982 CENT("DiffChange term=bold ctermbg=LightMagenta", 6983 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"), 6984 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan", 6985 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"), 6986 #endif 6987 #ifdef FEAT_WINDOWS 6988 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey", 6989 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"), 6990 #endif 6991 #ifdef FEAT_SYN_HL 6992 CENT("CursorColumn term=reverse ctermbg=LightGrey", 6993 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"), 6994 CENT("CursorLine term=underline cterm=underline", 6995 "CursorLine term=underline cterm=underline guibg=Grey90"), 6996 CENT("ColorColumn term=reverse ctermbg=LightRed", 6997 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"), 6998 #endif 6999 #ifdef FEAT_CONCEAL 7000 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey", 7001 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"), 7002 #endif 7003 #ifdef FEAT_AUTOCMD 7004 CENT("MatchParen term=reverse ctermbg=Cyan", 7005 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"), 7006 #endif 7007 #ifdef FEAT_GUI 7008 "Normal gui=NONE", 7009 #endif 7010 NULL 7011 }; 7012 7013 static char *(highlight_init_dark[]) = 7014 { 7015 CENT("Directory term=bold ctermfg=LightCyan", 7016 "Directory term=bold ctermfg=LightCyan guifg=Cyan"), 7017 CENT("LineNr term=underline ctermfg=Yellow", 7018 "LineNr term=underline ctermfg=Yellow guifg=Yellow"), 7019 CENT("CursorLineNr term=bold ctermfg=Yellow", 7020 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"), 7021 CENT("MoreMsg term=bold ctermfg=LightGreen", 7022 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"), 7023 CENT("Question term=standout ctermfg=LightGreen", 7024 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"), 7025 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black", 7026 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"), 7027 CENT("SpecialKey term=bold ctermfg=LightBlue", 7028 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"), 7029 #ifdef FEAT_SPELL 7030 CENT("SpellBad term=reverse ctermbg=Red", 7031 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"), 7032 CENT("SpellCap term=reverse ctermbg=Blue", 7033 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"), 7034 CENT("SpellRare term=reverse ctermbg=Magenta", 7035 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"), 7036 CENT("SpellLocal term=underline ctermbg=Cyan", 7037 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"), 7038 #endif 7039 #ifdef FEAT_INS_EXPAND 7040 CENT("PmenuThumb ctermbg=White", 7041 "PmenuThumb ctermbg=White guibg=White"), 7042 CENT("Pmenu ctermbg=Magenta ctermfg=Black", 7043 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"), 7044 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey", 7045 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"), 7046 #endif 7047 CENT("Title term=bold ctermfg=LightMagenta", 7048 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"), 7049 CENT("WarningMsg term=standout ctermfg=LightRed", 7050 "WarningMsg term=standout ctermfg=LightRed guifg=Red"), 7051 #ifdef FEAT_WILDMENU 7052 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black", 7053 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"), 7054 #endif 7055 #ifdef FEAT_FOLDING 7056 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan", 7057 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"), 7058 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan", 7059 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"), 7060 #endif 7061 #ifdef FEAT_SIGNS 7062 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan", 7063 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"), 7064 #endif 7065 CENT("Visual term=reverse", 7066 "Visual term=reverse guibg=DarkGrey"), 7067 #ifdef FEAT_DIFF 7068 CENT("DiffAdd term=bold ctermbg=DarkBlue", 7069 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"), 7070 CENT("DiffChange term=bold ctermbg=DarkMagenta", 7071 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"), 7072 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan", 7073 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"), 7074 #endif 7075 #ifdef FEAT_WINDOWS 7076 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey", 7077 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"), 7078 #endif 7079 #ifdef FEAT_SYN_HL 7080 CENT("CursorColumn term=reverse ctermbg=DarkGrey", 7081 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"), 7082 CENT("CursorLine term=underline cterm=underline", 7083 "CursorLine term=underline cterm=underline guibg=Grey40"), 7084 CENT("ColorColumn term=reverse ctermbg=DarkRed", 7085 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"), 7086 #endif 7087 #ifdef FEAT_AUTOCMD 7088 CENT("MatchParen term=reverse ctermbg=DarkCyan", 7089 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"), 7090 #endif 7091 #ifdef FEAT_CONCEAL 7092 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey", 7093 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"), 7094 #endif 7095 #ifdef FEAT_GUI 7096 "Normal gui=NONE", 7097 #endif 7098 NULL 7099 }; 7100 7101 void 7102 init_highlight( 7103 int both, /* include groups where 'bg' doesn't matter */ 7104 int reset) /* clear group first */ 7105 { 7106 int i; 7107 char **pp; 7108 static int had_both = FALSE; 7109 #ifdef FEAT_EVAL 7110 char_u *p; 7111 7112 /* 7113 * Try finding the color scheme file. Used when a color file was loaded 7114 * and 'background' or 't_Co' is changed. 7115 */ 7116 p = get_var_value((char_u *)"g:colors_name"); 7117 if (p != NULL) 7118 { 7119 /* The value of g:colors_name could be freed when sourcing the script, 7120 * making "p" invalid, so copy it. */ 7121 char_u *copy_p = vim_strsave(p); 7122 int r; 7123 7124 if (copy_p != NULL) 7125 { 7126 r = load_colors(copy_p); 7127 vim_free(copy_p); 7128 if (r == OK) 7129 return; 7130 } 7131 } 7132 7133 #endif 7134 7135 /* 7136 * Didn't use a color file, use the compiled-in colors. 7137 */ 7138 if (both) 7139 { 7140 had_both = TRUE; 7141 pp = highlight_init_both; 7142 for (i = 0; pp[i] != NULL; ++i) 7143 do_highlight((char_u *)pp[i], reset, TRUE); 7144 } 7145 else if (!had_both) 7146 /* Don't do anything before the call with both == TRUE from main(). 7147 * Not everything has been setup then, and that call will overrule 7148 * everything anyway. */ 7149 return; 7150 7151 if (*p_bg == 'l') 7152 pp = highlight_init_light; 7153 else 7154 pp = highlight_init_dark; 7155 for (i = 0; pp[i] != NULL; ++i) 7156 do_highlight((char_u *)pp[i], reset, TRUE); 7157 7158 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it 7159 * depend on the number of colors available. 7160 * With 8 colors brown is equal to yellow, need to use black for Search fg 7161 * to avoid Statement highlighted text disappears. 7162 * Clear the attributes, needed when changing the t_Co value. */ 7163 if (t_colors > 8) 7164 do_highlight((char_u *)(*p_bg == 'l' 7165 ? "Visual cterm=NONE ctermbg=LightGrey" 7166 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE); 7167 else 7168 { 7169 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE", 7170 FALSE, TRUE); 7171 if (*p_bg == 'l') 7172 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE); 7173 } 7174 7175 #ifdef FEAT_SYN_HL 7176 /* 7177 * If syntax highlighting is enabled load the highlighting for it. 7178 */ 7179 if (get_var_value((char_u *)"g:syntax_on") != NULL) 7180 { 7181 static int recursive = 0; 7182 7183 if (recursive >= 5) 7184 EMSG(_("E679: recursive loop loading syncolor.vim")); 7185 else 7186 { 7187 ++recursive; 7188 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL); 7189 --recursive; 7190 } 7191 } 7192 #endif 7193 } 7194 7195 /* 7196 * Load color file "name". 7197 * Return OK for success, FAIL for failure. 7198 */ 7199 int 7200 load_colors(char_u *name) 7201 { 7202 char_u *buf; 7203 int retval = FAIL; 7204 static int recursive = FALSE; 7205 7206 /* When being called recursively, this is probably because setting 7207 * 'background' caused the highlighting to be reloaded. This means it is 7208 * working, thus we should return OK. */ 7209 if (recursive) 7210 return OK; 7211 7212 recursive = TRUE; 7213 buf = alloc((unsigned)(STRLEN(name) + 12)); 7214 if (buf != NULL) 7215 { 7216 sprintf((char *)buf, "colors/%s.vim", name); 7217 retval = source_runtime(buf, DIP_START + DIP_OPT); 7218 vim_free(buf); 7219 #ifdef FEAT_AUTOCMD 7220 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf); 7221 #endif 7222 } 7223 recursive = FALSE; 7224 7225 return retval; 7226 } 7227 7228 static char *(color_names[28]) = { 7229 "Black", "DarkBlue", "DarkGreen", "DarkCyan", 7230 "DarkRed", "DarkMagenta", "Brown", "DarkYellow", 7231 "Gray", "Grey", "LightGray", "LightGrey", 7232 "DarkGray", "DarkGrey", 7233 "Blue", "LightBlue", "Green", "LightGreen", 7234 "Cyan", "LightCyan", "Red", "LightRed", "Magenta", 7235 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"}; 7236 /* indices: 7237 * 0, 1, 2, 3, 7238 * 4, 5, 6, 7, 7239 * 8, 9, 10, 11, 7240 * 12, 13, 7241 * 14, 15, 16, 17, 7242 * 18, 19, 20, 21, 22, 7243 * 23, 24, 25, 26, 27 */ 7244 static int color_numbers_16[28] = {0, 1, 2, 3, 7245 4, 5, 6, 6, 7246 7, 7, 7, 7, 7247 8, 8, 7248 9, 9, 10, 10, 7249 11, 11, 12, 12, 13, 7250 13, 14, 14, 15, -1}; 7251 /* for xterm with 88 colors... */ 7252 static int color_numbers_88[28] = {0, 4, 2, 6, 7253 1, 5, 32, 72, 7254 84, 84, 7, 7, 7255 82, 82, 7256 12, 43, 10, 61, 7257 14, 63, 9, 74, 13, 7258 75, 11, 78, 15, -1}; 7259 /* for xterm with 256 colors... */ 7260 static int color_numbers_256[28] = {0, 4, 2, 6, 7261 1, 5, 130, 130, 7262 248, 248, 7, 7, 7263 242, 242, 7264 12, 81, 10, 121, 7265 14, 159, 9, 224, 13, 7266 225, 11, 229, 15, -1}; 7267 /* for terminals with less than 16 colors... */ 7268 static int color_numbers_8[28] = {0, 4, 2, 6, 7269 1, 5, 3, 3, 7270 7, 7, 7, 7, 7271 0+8, 0+8, 7272 4+8, 4+8, 2+8, 2+8, 7273 6+8, 6+8, 1+8, 1+8, 5+8, 7274 5+8, 3+8, 3+8, 7+8, -1}; 7275 7276 /* 7277 * Lookup the "cterm" value to be used for color with index "idx" in 7278 * color_names[]. 7279 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8 7280 * colors, otherwise it will be unchanged. 7281 */ 7282 int 7283 lookup_color(int idx, int foreground, int *boldp) 7284 { 7285 int color = color_numbers_16[idx]; 7286 char_u *p; 7287 7288 /* Use the _16 table to check if it's a valid color name. */ 7289 if (color < 0) 7290 return -1; 7291 7292 if (t_colors == 8) 7293 { 7294 /* t_Co is 8: use the 8 colors table */ 7295 #if defined(__QNXNTO__) 7296 color = color_numbers_8_qansi[idx]; 7297 #else 7298 color = color_numbers_8[idx]; 7299 #endif 7300 if (foreground) 7301 { 7302 /* set/reset bold attribute to get light foreground 7303 * colors (on some terminals, e.g. "linux") */ 7304 if (color & 8) 7305 *boldp = TRUE; 7306 else 7307 *boldp = FALSE; 7308 } 7309 color &= 7; /* truncate to 8 colors */ 7310 } 7311 else if (t_colors == 16 || t_colors == 88 7312 || t_colors >= 256) 7313 { 7314 /* 7315 * Guess: if the termcap entry ends in 'm', it is 7316 * probably an xterm-like terminal. Use the changed 7317 * order for colors. 7318 */ 7319 if (*T_CAF != NUL) 7320 p = T_CAF; 7321 else 7322 p = T_CSF; 7323 if (*p != NUL && (t_colors > 256 7324 || *(p + STRLEN(p) - 1) == 'm')) 7325 { 7326 if (t_colors == 88) 7327 color = color_numbers_88[idx]; 7328 else if (t_colors >= 256) 7329 color = color_numbers_256[idx]; 7330 else 7331 color = color_numbers_8[idx]; 7332 } 7333 } 7334 return color; 7335 } 7336 7337 /* 7338 * Handle the ":highlight .." command. 7339 * When using ":hi clear" this is called recursively for each group with 7340 * "forceit" and "init" both TRUE. 7341 */ 7342 void 7343 do_highlight( 7344 char_u *line, 7345 int forceit, 7346 int init) /* TRUE when called for initializing */ 7347 { 7348 char_u *name_end; 7349 char_u *p; 7350 char_u *linep; 7351 char_u *key_start; 7352 char_u *arg_start; 7353 char_u *key = NULL, *arg = NULL; 7354 long i; 7355 int off; 7356 int len; 7357 int attr; 7358 int id; 7359 int idx; 7360 int dodefault = FALSE; 7361 int doclear = FALSE; 7362 int dolink = FALSE; 7363 int error = FALSE; 7364 int color; 7365 int is_normal_group = FALSE; /* "Normal" group */ 7366 #ifdef FEAT_GUI_X11 7367 int is_menu_group = FALSE; /* "Menu" group */ 7368 int is_scrollbar_group = FALSE; /* "Scrollbar" group */ 7369 int is_tooltip_group = FALSE; /* "Tooltip" group */ 7370 int do_colors = FALSE; /* need to update colors? */ 7371 #else 7372 # define is_menu_group 0 7373 # define is_tooltip_group 0 7374 #endif 7375 7376 /* 7377 * If no argument, list current highlighting. 7378 */ 7379 if (ends_excmd(*line)) 7380 { 7381 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i) 7382 /* TODO: only call when the group has attributes set */ 7383 highlight_list_one((int)i); 7384 return; 7385 } 7386 7387 /* 7388 * Isolate the name. 7389 */ 7390 name_end = skiptowhite(line); 7391 linep = skipwhite(name_end); 7392 7393 /* 7394 * Check for "default" argument. 7395 */ 7396 if (STRNCMP(line, "default", name_end - line) == 0) 7397 { 7398 dodefault = TRUE; 7399 line = linep; 7400 name_end = skiptowhite(line); 7401 linep = skipwhite(name_end); 7402 } 7403 7404 /* 7405 * Check for "clear" or "link" argument. 7406 */ 7407 if (STRNCMP(line, "clear", name_end - line) == 0) 7408 doclear = TRUE; 7409 if (STRNCMP(line, "link", name_end - line) == 0) 7410 dolink = TRUE; 7411 7412 /* 7413 * ":highlight {group-name}": list highlighting for one group. 7414 */ 7415 if (!doclear && !dolink && ends_excmd(*linep)) 7416 { 7417 id = syn_namen2id(line, (int)(name_end - line)); 7418 if (id == 0) 7419 EMSG2(_("E411: highlight group not found: %s"), line); 7420 else 7421 highlight_list_one(id); 7422 return; 7423 } 7424 7425 /* 7426 * Handle ":highlight link {from} {to}" command. 7427 */ 7428 if (dolink) 7429 { 7430 char_u *from_start = linep; 7431 char_u *from_end; 7432 char_u *to_start; 7433 char_u *to_end; 7434 int from_id; 7435 int to_id; 7436 7437 from_end = skiptowhite(from_start); 7438 to_start = skipwhite(from_end); 7439 to_end = skiptowhite(to_start); 7440 7441 if (ends_excmd(*from_start) || ends_excmd(*to_start)) 7442 { 7443 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""), 7444 from_start); 7445 return; 7446 } 7447 7448 if (!ends_excmd(*skipwhite(to_end))) 7449 { 7450 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start); 7451 return; 7452 } 7453 7454 from_id = syn_check_group(from_start, (int)(from_end - from_start)); 7455 if (STRNCMP(to_start, "NONE", 4) == 0) 7456 to_id = 0; 7457 else 7458 to_id = syn_check_group(to_start, (int)(to_end - to_start)); 7459 7460 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0)) 7461 { 7462 /* 7463 * Don't allow a link when there already is some highlighting 7464 * for the group, unless '!' is used 7465 */ 7466 if (to_id > 0 && !forceit && !init 7467 && hl_has_settings(from_id - 1, dodefault)) 7468 { 7469 if (sourcing_name == NULL && !dodefault) 7470 EMSG(_("E414: group has settings, highlight link ignored")); 7471 } 7472 else 7473 { 7474 if (!init) 7475 HL_TABLE()[from_id - 1].sg_set |= SG_LINK; 7476 HL_TABLE()[from_id - 1].sg_link = to_id; 7477 #ifdef FEAT_EVAL 7478 HL_TABLE()[from_id - 1].sg_scriptID = current_SID; 7479 #endif 7480 HL_TABLE()[from_id - 1].sg_cleared = FALSE; 7481 redraw_all_later(SOME_VALID); 7482 } 7483 } 7484 7485 /* Only call highlight_changed() once, after sourcing a syntax file */ 7486 need_highlight_changed = TRUE; 7487 7488 return; 7489 } 7490 7491 if (doclear) 7492 { 7493 /* 7494 * ":highlight clear [group]" command. 7495 */ 7496 line = linep; 7497 if (ends_excmd(*line)) 7498 { 7499 #ifdef FEAT_GUI 7500 /* First, we do not destroy the old values, but allocate the new 7501 * ones and update the display. THEN we destroy the old values. 7502 * If we destroy the old values first, then the old values 7503 * (such as GuiFont's or GuiFontset's) will still be displayed but 7504 * invalid because they were free'd. 7505 */ 7506 if (gui.in_use) 7507 { 7508 # ifdef FEAT_BEVAL_TIP 7509 gui_init_tooltip_font(); 7510 # endif 7511 # if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF)) 7512 gui_init_menu_font(); 7513 # endif 7514 } 7515 # if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11) 7516 gui_mch_def_colors(); 7517 # endif 7518 # ifdef FEAT_GUI_X11 7519 # ifdef FEAT_MENU 7520 7521 /* This only needs to be done when there is no Menu highlight 7522 * group defined by default, which IS currently the case. 7523 */ 7524 gui_mch_new_menu_colors(); 7525 # endif 7526 if (gui.in_use) 7527 { 7528 gui_new_scrollbar_colors(); 7529 # ifdef FEAT_BEVAL 7530 gui_mch_new_tooltip_colors(); 7531 # endif 7532 # ifdef FEAT_MENU 7533 gui_mch_new_menu_font(); 7534 # endif 7535 } 7536 # endif 7537 7538 /* Ok, we're done allocating the new default graphics items. 7539 * The screen should already be refreshed at this point. 7540 * It is now Ok to clear out the old data. 7541 */ 7542 #endif 7543 #ifdef FEAT_EVAL 7544 do_unlet((char_u *)"colors_name", TRUE); 7545 #endif 7546 restore_cterm_colors(); 7547 7548 /* 7549 * Clear all default highlight groups and load the defaults. 7550 */ 7551 for (idx = 0; idx < highlight_ga.ga_len; ++idx) 7552 highlight_clear(idx); 7553 init_highlight(TRUE, TRUE); 7554 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 7555 if (USE_24BIT) 7556 highlight_gui_started(); 7557 #endif 7558 highlight_changed(); 7559 redraw_later_clear(); 7560 return; 7561 } 7562 name_end = skiptowhite(line); 7563 linep = skipwhite(name_end); 7564 } 7565 7566 /* 7567 * Find the group name in the table. If it does not exist yet, add it. 7568 */ 7569 id = syn_check_group(line, (int)(name_end - line)); 7570 if (id == 0) /* failed (out of memory) */ 7571 return; 7572 idx = id - 1; /* index is ID minus one */ 7573 7574 /* Return if "default" was used and the group already has settings. */ 7575 if (dodefault && hl_has_settings(idx, TRUE)) 7576 return; 7577 7578 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0) 7579 is_normal_group = TRUE; 7580 #ifdef FEAT_GUI_X11 7581 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0) 7582 is_menu_group = TRUE; 7583 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0) 7584 is_scrollbar_group = TRUE; 7585 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0) 7586 is_tooltip_group = TRUE; 7587 #endif 7588 7589 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */ 7590 if (doclear || (forceit && init)) 7591 { 7592 highlight_clear(idx); 7593 if (!doclear) 7594 HL_TABLE()[idx].sg_set = 0; 7595 } 7596 7597 if (!doclear) 7598 while (!ends_excmd(*linep)) 7599 { 7600 key_start = linep; 7601 if (*linep == '=') 7602 { 7603 EMSG2(_("E415: unexpected equal sign: %s"), key_start); 7604 error = TRUE; 7605 break; 7606 } 7607 7608 /* 7609 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or 7610 * "guibg"). 7611 */ 7612 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=') 7613 ++linep; 7614 vim_free(key); 7615 key = vim_strnsave_up(key_start, (int)(linep - key_start)); 7616 if (key == NULL) 7617 { 7618 error = TRUE; 7619 break; 7620 } 7621 linep = skipwhite(linep); 7622 7623 if (STRCMP(key, "NONE") == 0) 7624 { 7625 if (!init || HL_TABLE()[idx].sg_set == 0) 7626 { 7627 if (!init) 7628 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI; 7629 highlight_clear(idx); 7630 } 7631 continue; 7632 } 7633 7634 /* 7635 * Check for the equal sign. 7636 */ 7637 if (*linep != '=') 7638 { 7639 EMSG2(_("E416: missing equal sign: %s"), key_start); 7640 error = TRUE; 7641 break; 7642 } 7643 ++linep; 7644 7645 /* 7646 * Isolate the argument. 7647 */ 7648 linep = skipwhite(linep); 7649 if (*linep == '\'') /* guifg='color name' */ 7650 { 7651 arg_start = ++linep; 7652 linep = vim_strchr(linep, '\''); 7653 if (linep == NULL) 7654 { 7655 EMSG2(_(e_invarg2), key_start); 7656 error = TRUE; 7657 break; 7658 } 7659 } 7660 else 7661 { 7662 arg_start = linep; 7663 linep = skiptowhite(linep); 7664 } 7665 if (linep == arg_start) 7666 { 7667 EMSG2(_("E417: missing argument: %s"), key_start); 7668 error = TRUE; 7669 break; 7670 } 7671 vim_free(arg); 7672 arg = vim_strnsave(arg_start, (int)(linep - arg_start)); 7673 if (arg == NULL) 7674 { 7675 error = TRUE; 7676 break; 7677 } 7678 if (*linep == '\'') 7679 ++linep; 7680 7681 /* 7682 * Store the argument. 7683 */ 7684 if ( STRCMP(key, "TERM") == 0 7685 || STRCMP(key, "CTERM") == 0 7686 || STRCMP(key, "GUI") == 0) 7687 { 7688 attr = 0; 7689 off = 0; 7690 while (arg[off] != NUL) 7691 { 7692 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; ) 7693 { 7694 len = (int)STRLEN(hl_name_table[i]); 7695 if (STRNICMP(arg + off, hl_name_table[i], len) == 0) 7696 { 7697 attr |= hl_attr_table[i]; 7698 off += len; 7699 break; 7700 } 7701 } 7702 if (i < 0) 7703 { 7704 EMSG2(_("E418: Illegal value: %s"), arg); 7705 error = TRUE; 7706 break; 7707 } 7708 if (arg[off] == ',') /* another one follows */ 7709 ++off; 7710 } 7711 if (error) 7712 break; 7713 if (*key == 'T') 7714 { 7715 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM)) 7716 { 7717 if (!init) 7718 HL_TABLE()[idx].sg_set |= SG_TERM; 7719 HL_TABLE()[idx].sg_term = attr; 7720 } 7721 } 7722 else if (*key == 'C') 7723 { 7724 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) 7725 { 7726 if (!init) 7727 HL_TABLE()[idx].sg_set |= SG_CTERM; 7728 HL_TABLE()[idx].sg_cterm = attr; 7729 HL_TABLE()[idx].sg_cterm_bold = FALSE; 7730 } 7731 } 7732 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 7733 else 7734 { 7735 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) 7736 { 7737 if (!init) 7738 HL_TABLE()[idx].sg_set |= SG_GUI; 7739 HL_TABLE()[idx].sg_gui = attr; 7740 } 7741 } 7742 #endif 7743 } 7744 else if (STRCMP(key, "FONT") == 0) 7745 { 7746 /* in non-GUI fonts are simply ignored */ 7747 #ifdef FEAT_GUI 7748 if (!gui.shell_created) 7749 { 7750 /* GUI not started yet, always accept the name. */ 7751 vim_free(HL_TABLE()[idx].sg_font_name); 7752 HL_TABLE()[idx].sg_font_name = vim_strsave(arg); 7753 } 7754 else 7755 { 7756 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font; 7757 # ifdef FEAT_XFONTSET 7758 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset; 7759 # endif 7760 /* First, save the current font/fontset. 7761 * Then try to allocate the font/fontset. 7762 * If the allocation fails, HL_TABLE()[idx].sg_font OR 7763 * sg_fontset will be set to NOFONT or NOFONTSET respectively. 7764 */ 7765 7766 HL_TABLE()[idx].sg_font = NOFONT; 7767 # ifdef FEAT_XFONTSET 7768 HL_TABLE()[idx].sg_fontset = NOFONTSET; 7769 # endif 7770 hl_do_font(idx, arg, is_normal_group, is_menu_group, 7771 is_tooltip_group, FALSE); 7772 7773 # ifdef FEAT_XFONTSET 7774 if (HL_TABLE()[idx].sg_fontset != NOFONTSET) 7775 { 7776 /* New fontset was accepted. Free the old one, if there 7777 * was one. */ 7778 gui_mch_free_fontset(temp_sg_fontset); 7779 vim_free(HL_TABLE()[idx].sg_font_name); 7780 HL_TABLE()[idx].sg_font_name = vim_strsave(arg); 7781 } 7782 else 7783 HL_TABLE()[idx].sg_fontset = temp_sg_fontset; 7784 # endif 7785 if (HL_TABLE()[idx].sg_font != NOFONT) 7786 { 7787 /* New font was accepted. Free the old one, if there was 7788 * one. */ 7789 gui_mch_free_font(temp_sg_font); 7790 vim_free(HL_TABLE()[idx].sg_font_name); 7791 HL_TABLE()[idx].sg_font_name = vim_strsave(arg); 7792 } 7793 else 7794 HL_TABLE()[idx].sg_font = temp_sg_font; 7795 } 7796 #endif 7797 } 7798 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) 7799 { 7800 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) 7801 { 7802 if (!init) 7803 HL_TABLE()[idx].sg_set |= SG_CTERM; 7804 7805 /* When setting the foreground color, and previously the "bold" 7806 * flag was set for a light color, reset it now */ 7807 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold) 7808 { 7809 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; 7810 HL_TABLE()[idx].sg_cterm_bold = FALSE; 7811 } 7812 7813 if (VIM_ISDIGIT(*arg)) 7814 color = atoi((char *)arg); 7815 else if (STRICMP(arg, "fg") == 0) 7816 { 7817 if (cterm_normal_fg_color) 7818 color = cterm_normal_fg_color - 1; 7819 else 7820 { 7821 EMSG(_("E419: FG color unknown")); 7822 error = TRUE; 7823 break; 7824 } 7825 } 7826 else if (STRICMP(arg, "bg") == 0) 7827 { 7828 if (cterm_normal_bg_color > 0) 7829 color = cterm_normal_bg_color - 1; 7830 else 7831 { 7832 EMSG(_("E420: BG color unknown")); 7833 error = TRUE; 7834 break; 7835 } 7836 } 7837 else 7838 { 7839 int bold = MAYBE; 7840 7841 #if defined(__QNXNTO__) 7842 static int *color_numbers_8_qansi = color_numbers_8; 7843 /* On qnx, the 8 & 16 color arrays are the same */ 7844 if (STRNCMP(T_NAME, "qansi", 5) == 0) 7845 color_numbers_8_qansi = color_numbers_16; 7846 #endif 7847 7848 /* reduce calls to STRICMP a bit, it can be slow */ 7849 off = TOUPPER_ASC(*arg); 7850 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; ) 7851 if (off == color_names[i][0] 7852 && STRICMP(arg + 1, color_names[i] + 1) == 0) 7853 break; 7854 if (i < 0) 7855 { 7856 EMSG2(_("E421: Color name or number not recognized: %s"), key_start); 7857 error = TRUE; 7858 break; 7859 } 7860 7861 color = lookup_color(i, key[5] == 'F', &bold); 7862 7863 /* set/reset bold attribute to get light foreground 7864 * colors (on some terminals, e.g. "linux") */ 7865 if (bold == TRUE) 7866 { 7867 HL_TABLE()[idx].sg_cterm |= HL_BOLD; 7868 HL_TABLE()[idx].sg_cterm_bold = TRUE; 7869 } 7870 else if (bold == FALSE) 7871 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; 7872 } 7873 7874 /* Add one to the argument, to avoid zero. Zero is used for 7875 * "NONE", then "color" is -1. */ 7876 if (key[5] == 'F') 7877 { 7878 HL_TABLE()[idx].sg_cterm_fg = color + 1; 7879 if (is_normal_group) 7880 { 7881 cterm_normal_fg_color = color + 1; 7882 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD); 7883 #ifdef FEAT_GUI 7884 /* Don't do this if the GUI is used. */ 7885 if (!gui.in_use && !gui.starting) 7886 #endif 7887 { 7888 must_redraw = CLEAR; 7889 if (termcap_active && color >= 0) 7890 term_fg_color(color); 7891 } 7892 } 7893 } 7894 else 7895 { 7896 HL_TABLE()[idx].sg_cterm_bg = color + 1; 7897 if (is_normal_group) 7898 { 7899 cterm_normal_bg_color = color + 1; 7900 #ifdef FEAT_GUI 7901 /* Don't mess with 'background' if the GUI is used. */ 7902 if (!gui.in_use && !gui.starting) 7903 #endif 7904 { 7905 must_redraw = CLEAR; 7906 if (color >= 0) 7907 { 7908 int dark = -1; 7909 7910 if (termcap_active) 7911 term_bg_color(color); 7912 if (t_colors < 16) 7913 dark = (color == 0 || color == 4); 7914 /* Limit the heuristic to the standard 16 colors */ 7915 else if (color < 16) 7916 dark = (color < 7 || color == 8); 7917 /* Set the 'background' option if the value is 7918 * wrong. */ 7919 if (dark != -1 7920 && dark != (*p_bg == 'd') 7921 && !option_was_set((char_u *)"bg")) 7922 { 7923 set_option_value((char_u *)"bg", 0L, 7924 (char_u *)(dark ? "dark" : "light"), 0); 7925 reset_option_was_set((char_u *)"bg"); 7926 } 7927 } 7928 } 7929 } 7930 } 7931 } 7932 } 7933 else if (STRCMP(key, "GUIFG") == 0) 7934 { 7935 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 7936 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) 7937 { 7938 if (!init) 7939 HL_TABLE()[idx].sg_set |= SG_GUI; 7940 7941 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 7942 /* In GUI guifg colors are only used when recognized */ 7943 i = color_name2handle(arg); 7944 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT) 7945 { 7946 HL_TABLE()[idx].sg_gui_fg = i; 7947 # endif 7948 vim_free(HL_TABLE()[idx].sg_gui_fg_name); 7949 if (STRCMP(arg, "NONE") != 0) 7950 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg); 7951 else 7952 HL_TABLE()[idx].sg_gui_fg_name = NULL; 7953 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 7954 # ifdef FEAT_GUI_X11 7955 if (is_menu_group) 7956 gui.menu_fg_pixel = i; 7957 if (is_scrollbar_group) 7958 gui.scroll_fg_pixel = i; 7959 # ifdef FEAT_BEVAL 7960 if (is_tooltip_group) 7961 gui.tooltip_fg_pixel = i; 7962 # endif 7963 do_colors = TRUE; 7964 # endif 7965 } 7966 # endif 7967 } 7968 #endif 7969 } 7970 else if (STRCMP(key, "GUIBG") == 0) 7971 { 7972 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 7973 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) 7974 { 7975 if (!init) 7976 HL_TABLE()[idx].sg_set |= SG_GUI; 7977 7978 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 7979 /* In GUI guifg colors are only used when recognized */ 7980 i = color_name2handle(arg); 7981 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT) 7982 { 7983 HL_TABLE()[idx].sg_gui_bg = i; 7984 # endif 7985 vim_free(HL_TABLE()[idx].sg_gui_bg_name); 7986 if (STRCMP(arg, "NONE") != 0) 7987 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg); 7988 else 7989 HL_TABLE()[idx].sg_gui_bg_name = NULL; 7990 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 7991 # ifdef FEAT_GUI_X11 7992 if (is_menu_group) 7993 gui.menu_bg_pixel = i; 7994 if (is_scrollbar_group) 7995 gui.scroll_bg_pixel = i; 7996 # ifdef FEAT_BEVAL 7997 if (is_tooltip_group) 7998 gui.tooltip_bg_pixel = i; 7999 # endif 8000 do_colors = TRUE; 8001 # endif 8002 } 8003 # endif 8004 } 8005 #endif 8006 } 8007 else if (STRCMP(key, "GUISP") == 0) 8008 { 8009 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 8010 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) 8011 { 8012 if (!init) 8013 HL_TABLE()[idx].sg_set |= SG_GUI; 8014 8015 # ifdef FEAT_GUI 8016 i = color_name2handle(arg); 8017 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use) 8018 { 8019 HL_TABLE()[idx].sg_gui_sp = i; 8020 # endif 8021 vim_free(HL_TABLE()[idx].sg_gui_sp_name); 8022 if (STRCMP(arg, "NONE") != 0) 8023 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg); 8024 else 8025 HL_TABLE()[idx].sg_gui_sp_name = NULL; 8026 # ifdef FEAT_GUI 8027 } 8028 # endif 8029 } 8030 #endif 8031 } 8032 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0) 8033 { 8034 char_u buf[100]; 8035 char_u *tname; 8036 8037 if (!init) 8038 HL_TABLE()[idx].sg_set |= SG_TERM; 8039 8040 /* 8041 * The "start" and "stop" arguments can be a literal escape 8042 * sequence, or a comma separated list of terminal codes. 8043 */ 8044 if (STRNCMP(arg, "t_", 2) == 0) 8045 { 8046 off = 0; 8047 buf[0] = 0; 8048 while (arg[off] != NUL) 8049 { 8050 /* Isolate one termcap name */ 8051 for (len = 0; arg[off + len] && 8052 arg[off + len] != ','; ++len) 8053 ; 8054 tname = vim_strnsave(arg + off, len); 8055 if (tname == NULL) /* out of memory */ 8056 { 8057 error = TRUE; 8058 break; 8059 } 8060 /* lookup the escape sequence for the item */ 8061 p = get_term_code(tname); 8062 vim_free(tname); 8063 if (p == NULL) /* ignore non-existing things */ 8064 p = (char_u *)""; 8065 8066 /* Append it to the already found stuff */ 8067 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99) 8068 { 8069 EMSG2(_("E422: terminal code too long: %s"), arg); 8070 error = TRUE; 8071 break; 8072 } 8073 STRCAT(buf, p); 8074 8075 /* Advance to the next item */ 8076 off += len; 8077 if (arg[off] == ',') /* another one follows */ 8078 ++off; 8079 } 8080 } 8081 else 8082 { 8083 /* 8084 * Copy characters from arg[] to buf[], translating <> codes. 8085 */ 8086 for (p = arg, off = 0; off < 100 - 6 && *p; ) 8087 { 8088 len = trans_special(&p, buf + off, FALSE, FALSE); 8089 if (len > 0) /* recognized special char */ 8090 off += len; 8091 else /* copy as normal char */ 8092 buf[off++] = *p++; 8093 } 8094 buf[off] = NUL; 8095 } 8096 if (error) 8097 break; 8098 8099 if (STRCMP(buf, "NONE") == 0) /* resetting the value */ 8100 p = NULL; 8101 else 8102 p = vim_strsave(buf); 8103 if (key[2] == 'A') 8104 { 8105 vim_free(HL_TABLE()[idx].sg_start); 8106 HL_TABLE()[idx].sg_start = p; 8107 } 8108 else 8109 { 8110 vim_free(HL_TABLE()[idx].sg_stop); 8111 HL_TABLE()[idx].sg_stop = p; 8112 } 8113 } 8114 else 8115 { 8116 EMSG2(_("E423: Illegal argument: %s"), key_start); 8117 error = TRUE; 8118 break; 8119 } 8120 HL_TABLE()[idx].sg_cleared = FALSE; 8121 8122 /* 8123 * When highlighting has been given for a group, don't link it. 8124 */ 8125 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK)) 8126 HL_TABLE()[idx].sg_link = 0; 8127 8128 /* 8129 * Continue with next argument. 8130 */ 8131 linep = skipwhite(linep); 8132 } 8133 8134 /* 8135 * If there is an error, and it's a new entry, remove it from the table. 8136 */ 8137 if (error && idx == highlight_ga.ga_len) 8138 syn_unadd_group(); 8139 else 8140 { 8141 if (is_normal_group) 8142 { 8143 HL_TABLE()[idx].sg_term_attr = 0; 8144 HL_TABLE()[idx].sg_cterm_attr = 0; 8145 #ifdef FEAT_GUI 8146 HL_TABLE()[idx].sg_gui_attr = 0; 8147 /* 8148 * Need to update all groups, because they might be using "bg" 8149 * and/or "fg", which have been changed now. 8150 */ 8151 #endif 8152 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 8153 if (USE_24BIT) 8154 highlight_gui_started(); 8155 #endif 8156 } 8157 #ifdef FEAT_GUI_X11 8158 # ifdef FEAT_MENU 8159 else if (is_menu_group) 8160 { 8161 if (gui.in_use && do_colors) 8162 gui_mch_new_menu_colors(); 8163 } 8164 # endif 8165 else if (is_scrollbar_group) 8166 { 8167 if (gui.in_use && do_colors) 8168 gui_new_scrollbar_colors(); 8169 } 8170 # ifdef FEAT_BEVAL 8171 else if (is_tooltip_group) 8172 { 8173 if (gui.in_use && do_colors) 8174 gui_mch_new_tooltip_colors(); 8175 } 8176 # endif 8177 #endif 8178 else 8179 set_hl_attr(idx); 8180 #ifdef FEAT_EVAL 8181 HL_TABLE()[idx].sg_scriptID = current_SID; 8182 #endif 8183 redraw_all_later(NOT_VALID); 8184 } 8185 vim_free(key); 8186 vim_free(arg); 8187 8188 /* Only call highlight_changed() once, after sourcing a syntax file */ 8189 need_highlight_changed = TRUE; 8190 } 8191 8192 #if defined(EXITFREE) || defined(PROTO) 8193 void 8194 free_highlight(void) 8195 { 8196 int i; 8197 8198 for (i = 0; i < highlight_ga.ga_len; ++i) 8199 { 8200 highlight_clear(i); 8201 vim_free(HL_TABLE()[i].sg_name); 8202 vim_free(HL_TABLE()[i].sg_name_u); 8203 } 8204 ga_clear(&highlight_ga); 8205 } 8206 #endif 8207 8208 /* 8209 * Reset the cterm colors to what they were before Vim was started, if 8210 * possible. Otherwise reset them to zero. 8211 */ 8212 void 8213 restore_cterm_colors(void) 8214 { 8215 #if defined(WIN3264) && !defined(FEAT_GUI_W32) 8216 /* Since t_me has been set, this probably means that the user 8217 * wants to use this as default colors. Need to reset default 8218 * background/foreground colors. */ 8219 mch_set_normal_colors(); 8220 #else 8221 cterm_normal_fg_color = 0; 8222 cterm_normal_fg_bold = 0; 8223 cterm_normal_bg_color = 0; 8224 # ifdef FEAT_TERMGUICOLORS 8225 cterm_normal_fg_gui_color = INVALCOLOR; 8226 cterm_normal_bg_gui_color = INVALCOLOR; 8227 # endif 8228 #endif 8229 } 8230 8231 /* 8232 * Return TRUE if highlight group "idx" has any settings. 8233 * When "check_link" is TRUE also check for an existing link. 8234 */ 8235 static int 8236 hl_has_settings(int idx, int check_link) 8237 { 8238 return ( HL_TABLE()[idx].sg_term_attr != 0 8239 || HL_TABLE()[idx].sg_cterm_attr != 0 8240 || HL_TABLE()[idx].sg_cterm_fg != 0 8241 || HL_TABLE()[idx].sg_cterm_bg != 0 8242 #ifdef FEAT_GUI 8243 || HL_TABLE()[idx].sg_gui_attr != 0 8244 || HL_TABLE()[idx].sg_gui_fg_name != NULL 8245 || HL_TABLE()[idx].sg_gui_bg_name != NULL 8246 || HL_TABLE()[idx].sg_gui_sp_name != NULL 8247 || HL_TABLE()[idx].sg_font_name != NULL 8248 #endif 8249 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK))); 8250 } 8251 8252 /* 8253 * Clear highlighting for one group. 8254 */ 8255 static void 8256 highlight_clear(int idx) 8257 { 8258 HL_TABLE()[idx].sg_cleared = TRUE; 8259 8260 HL_TABLE()[idx].sg_term = 0; 8261 vim_free(HL_TABLE()[idx].sg_start); 8262 HL_TABLE()[idx].sg_start = NULL; 8263 vim_free(HL_TABLE()[idx].sg_stop); 8264 HL_TABLE()[idx].sg_stop = NULL; 8265 HL_TABLE()[idx].sg_term_attr = 0; 8266 HL_TABLE()[idx].sg_cterm = 0; 8267 HL_TABLE()[idx].sg_cterm_bold = FALSE; 8268 HL_TABLE()[idx].sg_cterm_fg = 0; 8269 HL_TABLE()[idx].sg_cterm_bg = 0; 8270 HL_TABLE()[idx].sg_cterm_attr = 0; 8271 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 8272 HL_TABLE()[idx].sg_gui = 0; 8273 vim_free(HL_TABLE()[idx].sg_gui_fg_name); 8274 HL_TABLE()[idx].sg_gui_fg_name = NULL; 8275 vim_free(HL_TABLE()[idx].sg_gui_bg_name); 8276 HL_TABLE()[idx].sg_gui_bg_name = NULL; 8277 vim_free(HL_TABLE()[idx].sg_gui_sp_name); 8278 HL_TABLE()[idx].sg_gui_sp_name = NULL; 8279 #endif 8280 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 8281 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR; 8282 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR; 8283 #endif 8284 #ifdef FEAT_GUI 8285 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR; 8286 gui_mch_free_font(HL_TABLE()[idx].sg_font); 8287 HL_TABLE()[idx].sg_font = NOFONT; 8288 # ifdef FEAT_XFONTSET 8289 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset); 8290 HL_TABLE()[idx].sg_fontset = NOFONTSET; 8291 # endif 8292 vim_free(HL_TABLE()[idx].sg_font_name); 8293 HL_TABLE()[idx].sg_font_name = NULL; 8294 HL_TABLE()[idx].sg_gui_attr = 0; 8295 #endif 8296 #ifdef FEAT_EVAL 8297 /* Clear the script ID only when there is no link, since that is not 8298 * cleared. */ 8299 if (HL_TABLE()[idx].sg_link == 0) 8300 HL_TABLE()[idx].sg_scriptID = 0; 8301 #endif 8302 } 8303 8304 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO) 8305 /* 8306 * Set the normal foreground and background colors according to the "Normal" 8307 * highlighting group. For X11 also set "Menu", "Scrollbar", and 8308 * "Tooltip" colors. 8309 */ 8310 void 8311 set_normal_colors(void) 8312 { 8313 #ifdef FEAT_GUI 8314 # ifdef FEAT_TERMGUICOLORS 8315 if (gui.in_use) 8316 # endif 8317 { 8318 if (set_group_colors((char_u *)"Normal", 8319 &gui.norm_pixel, &gui.back_pixel, 8320 FALSE, TRUE, FALSE)) 8321 { 8322 gui_mch_new_colors(); 8323 must_redraw = CLEAR; 8324 } 8325 # ifdef FEAT_GUI_X11 8326 if (set_group_colors((char_u *)"Menu", 8327 &gui.menu_fg_pixel, &gui.menu_bg_pixel, 8328 TRUE, FALSE, FALSE)) 8329 { 8330 # ifdef FEAT_MENU 8331 gui_mch_new_menu_colors(); 8332 # endif 8333 must_redraw = CLEAR; 8334 } 8335 # ifdef FEAT_BEVAL 8336 if (set_group_colors((char_u *)"Tooltip", 8337 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel, 8338 FALSE, FALSE, TRUE)) 8339 { 8340 # ifdef FEAT_TOOLBAR 8341 gui_mch_new_tooltip_colors(); 8342 # endif 8343 must_redraw = CLEAR; 8344 } 8345 # endif 8346 if (set_group_colors((char_u *)"Scrollbar", 8347 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel, 8348 FALSE, FALSE, FALSE)) 8349 { 8350 gui_new_scrollbar_colors(); 8351 must_redraw = CLEAR; 8352 } 8353 # endif 8354 } 8355 #endif 8356 #ifdef FEAT_TERMGUICOLORS 8357 # ifdef FEAT_GUI 8358 else 8359 # endif 8360 { 8361 int idx; 8362 8363 idx = syn_name2id((char_u *)"Normal") - 1; 8364 if (idx >= 0) 8365 { 8366 gui_do_one_color(idx, FALSE, FALSE); 8367 8368 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR) 8369 { 8370 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg; 8371 must_redraw = CLEAR; 8372 } 8373 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR) 8374 { 8375 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg; 8376 must_redraw = CLEAR; 8377 } 8378 } 8379 } 8380 #endif 8381 } 8382 #endif 8383 8384 #if defined(FEAT_GUI) || defined(PROTO) 8385 /* 8386 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar". 8387 */ 8388 static int 8389 set_group_colors( 8390 char_u *name, 8391 guicolor_T *fgp, 8392 guicolor_T *bgp, 8393 int do_menu, 8394 int use_norm, 8395 int do_tooltip) 8396 { 8397 int idx; 8398 8399 idx = syn_name2id(name) - 1; 8400 if (idx >= 0) 8401 { 8402 gui_do_one_color(idx, do_menu, do_tooltip); 8403 8404 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR) 8405 *fgp = HL_TABLE()[idx].sg_gui_fg; 8406 else if (use_norm) 8407 *fgp = gui.def_norm_pixel; 8408 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR) 8409 *bgp = HL_TABLE()[idx].sg_gui_bg; 8410 else if (use_norm) 8411 *bgp = gui.def_back_pixel; 8412 return TRUE; 8413 } 8414 return FALSE; 8415 } 8416 8417 /* 8418 * Get the font of the "Normal" group. 8419 * Returns "" when it's not found or not set. 8420 */ 8421 char_u * 8422 hl_get_font_name(void) 8423 { 8424 int id; 8425 char_u *s; 8426 8427 id = syn_name2id((char_u *)"Normal"); 8428 if (id > 0) 8429 { 8430 s = HL_TABLE()[id - 1].sg_font_name; 8431 if (s != NULL) 8432 return s; 8433 } 8434 return (char_u *)""; 8435 } 8436 8437 /* 8438 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has 8439 * actually chosen to be used. 8440 */ 8441 void 8442 hl_set_font_name(char_u *font_name) 8443 { 8444 int id; 8445 8446 id = syn_name2id((char_u *)"Normal"); 8447 if (id > 0) 8448 { 8449 vim_free(HL_TABLE()[id - 1].sg_font_name); 8450 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name); 8451 } 8452 } 8453 8454 /* 8455 * Set background color for "Normal" group. Called by gui_set_bg_color() 8456 * when the color is known. 8457 */ 8458 void 8459 hl_set_bg_color_name( 8460 char_u *name) /* must have been allocated */ 8461 { 8462 int id; 8463 8464 if (name != NULL) 8465 { 8466 id = syn_name2id((char_u *)"Normal"); 8467 if (id > 0) 8468 { 8469 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name); 8470 HL_TABLE()[id - 1].sg_gui_bg_name = name; 8471 } 8472 } 8473 } 8474 8475 /* 8476 * Set foreground color for "Normal" group. Called by gui_set_fg_color() 8477 * when the color is known. 8478 */ 8479 void 8480 hl_set_fg_color_name( 8481 char_u *name) /* must have been allocated */ 8482 { 8483 int id; 8484 8485 if (name != NULL) 8486 { 8487 id = syn_name2id((char_u *)"Normal"); 8488 if (id > 0) 8489 { 8490 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name); 8491 HL_TABLE()[id - 1].sg_gui_fg_name = name; 8492 } 8493 } 8494 } 8495 8496 /* 8497 * Return the handle for a font name. 8498 * Returns NOFONT when failed. 8499 */ 8500 static GuiFont 8501 font_name2handle(char_u *name) 8502 { 8503 if (STRCMP(name, "NONE") == 0) 8504 return NOFONT; 8505 8506 return gui_mch_get_font(name, TRUE); 8507 } 8508 8509 # ifdef FEAT_XFONTSET 8510 /* 8511 * Return the handle for a fontset name. 8512 * Returns NOFONTSET when failed. 8513 */ 8514 static GuiFontset 8515 fontset_name2handle(char_u *name, int fixed_width) 8516 { 8517 if (STRCMP(name, "NONE") == 0) 8518 return NOFONTSET; 8519 8520 return gui_mch_get_fontset(name, TRUE, fixed_width); 8521 } 8522 # endif 8523 8524 /* 8525 * Get the font or fontset for one highlight group. 8526 */ 8527 static void 8528 hl_do_font( 8529 int idx, 8530 char_u *arg, 8531 int do_normal, /* set normal font */ 8532 int do_menu UNUSED, /* set menu font */ 8533 int do_tooltip UNUSED, /* set tooltip font */ 8534 int free_font) /* free current font/fontset */ 8535 { 8536 # ifdef FEAT_XFONTSET 8537 /* If 'guifontset' is not empty, first try using the name as a 8538 * fontset. If that doesn't work, use it as a font name. */ 8539 if (*p_guifontset != NUL 8540 # ifdef FONTSET_ALWAYS 8541 || do_menu 8542 # endif 8543 # ifdef FEAT_BEVAL_TIP 8544 /* In Athena & Motif, the Tooltip highlight group is always a fontset */ 8545 || do_tooltip 8546 # endif 8547 ) 8548 { 8549 if (free_font) 8550 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset); 8551 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0 8552 # ifdef FONTSET_ALWAYS 8553 || do_menu 8554 # endif 8555 # ifdef FEAT_BEVAL_TIP 8556 || do_tooltip 8557 # endif 8558 ); 8559 } 8560 if (HL_TABLE()[idx].sg_fontset != NOFONTSET) 8561 { 8562 /* If it worked and it's the Normal group, use it as the normal 8563 * fontset. Same for the Menu group. */ 8564 if (do_normal) 8565 gui_init_font(arg, TRUE); 8566 # if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU) 8567 if (do_menu) 8568 { 8569 # ifdef FONTSET_ALWAYS 8570 gui.menu_fontset = HL_TABLE()[idx].sg_fontset; 8571 # else 8572 /* YIKES! This is a bug waiting to crash the program */ 8573 gui.menu_font = HL_TABLE()[idx].sg_fontset; 8574 # endif 8575 gui_mch_new_menu_font(); 8576 } 8577 # ifdef FEAT_BEVAL 8578 if (do_tooltip) 8579 { 8580 /* The Athena widget set cannot currently handle switching between 8581 * displaying a single font and a fontset. 8582 * If the XtNinternational resource is set to True at widget 8583 * creation, then a fontset is always used, otherwise an 8584 * XFontStruct is used. 8585 */ 8586 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset; 8587 gui_mch_new_tooltip_font(); 8588 } 8589 # endif 8590 # endif 8591 } 8592 else 8593 # endif 8594 { 8595 if (free_font) 8596 gui_mch_free_font(HL_TABLE()[idx].sg_font); 8597 HL_TABLE()[idx].sg_font = font_name2handle(arg); 8598 /* If it worked and it's the Normal group, use it as the 8599 * normal font. Same for the Menu group. */ 8600 if (HL_TABLE()[idx].sg_font != NOFONT) 8601 { 8602 if (do_normal) 8603 gui_init_font(arg, FALSE); 8604 #ifndef FONTSET_ALWAYS 8605 # if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU) 8606 if (do_menu) 8607 { 8608 gui.menu_font = HL_TABLE()[idx].sg_font; 8609 gui_mch_new_menu_font(); 8610 } 8611 # endif 8612 #endif 8613 } 8614 } 8615 } 8616 8617 #endif /* FEAT_GUI */ 8618 8619 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO) 8620 /* 8621 * Return the handle for a color name. 8622 * Returns INVALCOLOR when failed. 8623 */ 8624 static guicolor_T 8625 color_name2handle(char_u *name) 8626 { 8627 if (STRCMP(name, "NONE") == 0) 8628 return INVALCOLOR; 8629 8630 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0) 8631 { 8632 #if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI) 8633 if (gui.in_use) 8634 #endif 8635 #ifdef FEAT_GUI 8636 return gui.norm_pixel; 8637 #endif 8638 #ifdef FEAT_TERMGUICOLORS 8639 if (cterm_normal_fg_gui_color != INVALCOLOR) 8640 return cterm_normal_fg_gui_color; 8641 /* Guess that the foreground is black or white. */ 8642 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white")); 8643 #endif 8644 } 8645 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0) 8646 { 8647 #if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI) 8648 if (gui.in_use) 8649 #endif 8650 #ifdef FEAT_GUI 8651 return gui.back_pixel; 8652 #endif 8653 #ifdef FEAT_TERMGUICOLORS 8654 if (cterm_normal_bg_gui_color != INVALCOLOR) 8655 return cterm_normal_bg_gui_color; 8656 /* Guess that the background is white or black. */ 8657 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black")); 8658 #endif 8659 } 8660 8661 return GUI_GET_COLOR(name); 8662 } 8663 #endif 8664 8665 /* 8666 * Table with the specifications for an attribute number. 8667 * Note that this table is used by ALL buffers. This is required because the 8668 * GUI can redraw at any time for any buffer. 8669 */ 8670 static garray_T term_attr_table = {0, 0, 0, 0, NULL}; 8671 8672 #define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx] 8673 8674 static garray_T cterm_attr_table = {0, 0, 0, 0, NULL}; 8675 8676 #define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx] 8677 8678 #ifdef FEAT_GUI 8679 static garray_T gui_attr_table = {0, 0, 0, 0, NULL}; 8680 8681 #define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx] 8682 #endif 8683 8684 /* 8685 * Return the attr number for a set of colors and font. 8686 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table 8687 * if the combination is new. 8688 * Return 0 for error (no more room). 8689 */ 8690 static int 8691 get_attr_entry(garray_T *table, attrentry_T *aep) 8692 { 8693 int i; 8694 attrentry_T *taep; 8695 static int recursive = FALSE; 8696 8697 /* 8698 * Init the table, in case it wasn't done yet. 8699 */ 8700 table->ga_itemsize = sizeof(attrentry_T); 8701 table->ga_growsize = 7; 8702 8703 /* 8704 * Try to find an entry with the same specifications. 8705 */ 8706 for (i = 0; i < table->ga_len; ++i) 8707 { 8708 taep = &(((attrentry_T *)table->ga_data)[i]); 8709 if ( aep->ae_attr == taep->ae_attr 8710 && ( 8711 #ifdef FEAT_GUI 8712 (table == &gui_attr_table 8713 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color 8714 && aep->ae_u.gui.bg_color 8715 == taep->ae_u.gui.bg_color 8716 && aep->ae_u.gui.sp_color 8717 == taep->ae_u.gui.sp_color 8718 && aep->ae_u.gui.font == taep->ae_u.gui.font 8719 # ifdef FEAT_XFONTSET 8720 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset 8721 # endif 8722 )) 8723 || 8724 #endif 8725 (table == &term_attr_table 8726 && (aep->ae_u.term.start == NULL) 8727 == (taep->ae_u.term.start == NULL) 8728 && (aep->ae_u.term.start == NULL 8729 || STRCMP(aep->ae_u.term.start, 8730 taep->ae_u.term.start) == 0) 8731 && (aep->ae_u.term.stop == NULL) 8732 == (taep->ae_u.term.stop == NULL) 8733 && (aep->ae_u.term.stop == NULL 8734 || STRCMP(aep->ae_u.term.stop, 8735 taep->ae_u.term.stop) == 0)) 8736 || (table == &cterm_attr_table 8737 && aep->ae_u.cterm.fg_color 8738 == taep->ae_u.cterm.fg_color 8739 && aep->ae_u.cterm.bg_color 8740 == taep->ae_u.cterm.bg_color 8741 #ifdef FEAT_TERMGUICOLORS 8742 && aep->ae_u.cterm.fg_rgb 8743 == taep->ae_u.cterm.fg_rgb 8744 && aep->ae_u.cterm.bg_rgb 8745 == taep->ae_u.cterm.bg_rgb 8746 #endif 8747 ))) 8748 8749 return i + ATTR_OFF; 8750 } 8751 8752 if (table->ga_len + ATTR_OFF > MAX_TYPENR) 8753 { 8754 /* 8755 * Running out of attribute entries! remove all attributes, and 8756 * compute new ones for all groups. 8757 * When called recursively, we are really out of numbers. 8758 */ 8759 if (recursive) 8760 { 8761 EMSG(_("E424: Too many different highlighting attributes in use")); 8762 return 0; 8763 } 8764 recursive = TRUE; 8765 8766 clear_hl_tables(); 8767 8768 must_redraw = CLEAR; 8769 8770 for (i = 0; i < highlight_ga.ga_len; ++i) 8771 set_hl_attr(i); 8772 8773 recursive = FALSE; 8774 } 8775 8776 /* 8777 * This is a new combination of colors and font, add an entry. 8778 */ 8779 if (ga_grow(table, 1) == FAIL) 8780 return 0; 8781 8782 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]); 8783 vim_memset(taep, 0, sizeof(attrentry_T)); 8784 taep->ae_attr = aep->ae_attr; 8785 #ifdef FEAT_GUI 8786 if (table == &gui_attr_table) 8787 { 8788 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color; 8789 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color; 8790 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color; 8791 taep->ae_u.gui.font = aep->ae_u.gui.font; 8792 # ifdef FEAT_XFONTSET 8793 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset; 8794 # endif 8795 } 8796 #endif 8797 if (table == &term_attr_table) 8798 { 8799 if (aep->ae_u.term.start == NULL) 8800 taep->ae_u.term.start = NULL; 8801 else 8802 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start); 8803 if (aep->ae_u.term.stop == NULL) 8804 taep->ae_u.term.stop = NULL; 8805 else 8806 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop); 8807 } 8808 else if (table == &cterm_attr_table) 8809 { 8810 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color; 8811 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color; 8812 #ifdef FEAT_TERMGUICOLORS 8813 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb; 8814 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb; 8815 #endif 8816 } 8817 ++table->ga_len; 8818 return (table->ga_len - 1 + ATTR_OFF); 8819 } 8820 8821 /* 8822 * Get an attribute index for a cterm entry. 8823 * Uses an existing entry when possible or adds one when needed. 8824 */ 8825 int 8826 get_cterm_attr_idx(int attr, int fg, int bg) 8827 { 8828 attrentry_T at_en; 8829 8830 vim_memset(&at_en, 0, sizeof(attrentry_T)); 8831 at_en.ae_attr = attr; 8832 at_en.ae_u.cterm.fg_color = fg; 8833 at_en.ae_u.cterm.bg_color = bg; 8834 return get_attr_entry(&cterm_attr_table, &at_en); 8835 } 8836 8837 #if defined(FEAT_TERMGUICOLORS) || defined(PROTO) 8838 /* 8839 * Get an attribute index for a 'termguicolors' entry. 8840 * Uses an existing entry when possible or adds one when needed. 8841 */ 8842 int 8843 get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg) 8844 { 8845 attrentry_T at_en; 8846 8847 vim_memset(&at_en, 0, sizeof(attrentry_T)); 8848 at_en.ae_attr = attr; 8849 at_en.ae_u.cterm.fg_rgb = fg; 8850 at_en.ae_u.cterm.bg_rgb = bg; 8851 return get_attr_entry(&cterm_attr_table, &at_en); 8852 } 8853 #endif 8854 8855 #if defined(FEAT_GUI) || defined(PROTO) 8856 /* 8857 * Get an attribute index for a cterm entry. 8858 * Uses an existing entry when possible or adds one when needed. 8859 */ 8860 int 8861 get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg) 8862 { 8863 attrentry_T at_en; 8864 8865 vim_memset(&at_en, 0, sizeof(attrentry_T)); 8866 at_en.ae_attr = attr; 8867 at_en.ae_u.gui.fg_color = fg; 8868 at_en.ae_u.gui.bg_color = bg; 8869 return get_attr_entry(&gui_attr_table, &at_en); 8870 } 8871 #endif 8872 8873 /* 8874 * Clear all highlight tables. 8875 */ 8876 void 8877 clear_hl_tables(void) 8878 { 8879 int i; 8880 attrentry_T *taep; 8881 8882 #ifdef FEAT_GUI 8883 ga_clear(&gui_attr_table); 8884 #endif 8885 for (i = 0; i < term_attr_table.ga_len; ++i) 8886 { 8887 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]); 8888 vim_free(taep->ae_u.term.start); 8889 vim_free(taep->ae_u.term.stop); 8890 } 8891 ga_clear(&term_attr_table); 8892 ga_clear(&cterm_attr_table); 8893 } 8894 8895 #if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO) 8896 /* 8897 * Combine special attributes (e.g., for spelling) with other attributes 8898 * (e.g., for syntax highlighting). 8899 * "prim_attr" overrules "char_attr". 8900 * This creates a new group when required. 8901 * Since we expect there to be few spelling mistakes we don't cache the 8902 * result. 8903 * Return the resulting attributes. 8904 */ 8905 int 8906 hl_combine_attr(int char_attr, int prim_attr) 8907 { 8908 attrentry_T *char_aep = NULL; 8909 attrentry_T *spell_aep; 8910 attrentry_T new_en; 8911 8912 if (char_attr == 0) 8913 return prim_attr; 8914 if (char_attr <= HL_ALL && prim_attr <= HL_ALL) 8915 return char_attr | prim_attr; 8916 #ifdef FEAT_GUI 8917 if (gui.in_use) 8918 { 8919 if (char_attr > HL_ALL) 8920 char_aep = syn_gui_attr2entry(char_attr); 8921 if (char_aep != NULL) 8922 new_en = *char_aep; 8923 else 8924 { 8925 vim_memset(&new_en, 0, sizeof(new_en)); 8926 new_en.ae_u.gui.fg_color = INVALCOLOR; 8927 new_en.ae_u.gui.bg_color = INVALCOLOR; 8928 new_en.ae_u.gui.sp_color = INVALCOLOR; 8929 if (char_attr <= HL_ALL) 8930 new_en.ae_attr = char_attr; 8931 } 8932 8933 if (prim_attr <= HL_ALL) 8934 new_en.ae_attr |= prim_attr; 8935 else 8936 { 8937 spell_aep = syn_gui_attr2entry(prim_attr); 8938 if (spell_aep != NULL) 8939 { 8940 new_en.ae_attr |= spell_aep->ae_attr; 8941 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR) 8942 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color; 8943 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR) 8944 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color; 8945 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR) 8946 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color; 8947 if (spell_aep->ae_u.gui.font != NOFONT) 8948 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font; 8949 # ifdef FEAT_XFONTSET 8950 if (spell_aep->ae_u.gui.fontset != NOFONTSET) 8951 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset; 8952 # endif 8953 } 8954 } 8955 return get_attr_entry(&gui_attr_table, &new_en); 8956 } 8957 #endif 8958 8959 if (IS_CTERM) 8960 { 8961 if (char_attr > HL_ALL) 8962 char_aep = syn_cterm_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 #ifdef FEAT_TERMGUICOLORS 8969 new_en.ae_u.cterm.bg_rgb = INVALCOLOR; 8970 new_en.ae_u.cterm.fg_rgb = INVALCOLOR; 8971 #endif 8972 if (char_attr <= HL_ALL) 8973 new_en.ae_attr = char_attr; 8974 } 8975 8976 if (prim_attr <= HL_ALL) 8977 new_en.ae_attr |= prim_attr; 8978 else 8979 { 8980 spell_aep = syn_cterm_attr2entry(prim_attr); 8981 if (spell_aep != NULL) 8982 { 8983 new_en.ae_attr |= spell_aep->ae_attr; 8984 if (spell_aep->ae_u.cterm.fg_color > 0) 8985 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color; 8986 if (spell_aep->ae_u.cterm.bg_color > 0) 8987 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color; 8988 #ifdef FEAT_TERMGUICOLORS 8989 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR) 8990 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb; 8991 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR) 8992 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb; 8993 #endif 8994 } 8995 } 8996 return get_attr_entry(&cterm_attr_table, &new_en); 8997 } 8998 8999 if (char_attr > HL_ALL) 9000 char_aep = syn_term_attr2entry(char_attr); 9001 if (char_aep != NULL) 9002 new_en = *char_aep; 9003 else 9004 { 9005 vim_memset(&new_en, 0, sizeof(new_en)); 9006 if (char_attr <= HL_ALL) 9007 new_en.ae_attr = char_attr; 9008 } 9009 9010 if (prim_attr <= HL_ALL) 9011 new_en.ae_attr |= prim_attr; 9012 else 9013 { 9014 spell_aep = syn_term_attr2entry(prim_attr); 9015 if (spell_aep != NULL) 9016 { 9017 new_en.ae_attr |= spell_aep->ae_attr; 9018 if (spell_aep->ae_u.term.start != NULL) 9019 { 9020 new_en.ae_u.term.start = spell_aep->ae_u.term.start; 9021 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop; 9022 } 9023 } 9024 } 9025 return get_attr_entry(&term_attr_table, &new_en); 9026 } 9027 #endif 9028 9029 #ifdef FEAT_GUI 9030 9031 attrentry_T * 9032 syn_gui_attr2entry(int attr) 9033 { 9034 attr -= ATTR_OFF; 9035 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */ 9036 return NULL; 9037 return &(GUI_ATTR_ENTRY(attr)); 9038 } 9039 #endif /* FEAT_GUI */ 9040 9041 /* 9042 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr. 9043 * Only to be used when "attr" > HL_ALL. 9044 */ 9045 int 9046 syn_attr2attr(int attr) 9047 { 9048 attrentry_T *aep; 9049 9050 #ifdef FEAT_GUI 9051 if (gui.in_use) 9052 aep = syn_gui_attr2entry(attr); 9053 else 9054 #endif 9055 if (IS_CTERM) 9056 aep = syn_cterm_attr2entry(attr); 9057 else 9058 aep = syn_term_attr2entry(attr); 9059 9060 if (aep == NULL) /* highlighting not set */ 9061 return 0; 9062 return aep->ae_attr; 9063 } 9064 9065 9066 attrentry_T * 9067 syn_term_attr2entry(int attr) 9068 { 9069 attr -= ATTR_OFF; 9070 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */ 9071 return NULL; 9072 return &(TERM_ATTR_ENTRY(attr)); 9073 } 9074 9075 attrentry_T * 9076 syn_cterm_attr2entry(int attr) 9077 { 9078 attr -= ATTR_OFF; 9079 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */ 9080 return NULL; 9081 return &(CTERM_ATTR_ENTRY(attr)); 9082 } 9083 9084 #define LIST_ATTR 1 9085 #define LIST_STRING 2 9086 #define LIST_INT 3 9087 9088 static void 9089 highlight_list_one(int id) 9090 { 9091 struct hl_group *sgp; 9092 int didh = FALSE; 9093 9094 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */ 9095 9096 didh = highlight_list_arg(id, didh, LIST_ATTR, 9097 sgp->sg_term, NULL, "term"); 9098 didh = highlight_list_arg(id, didh, LIST_STRING, 9099 0, sgp->sg_start, "start"); 9100 didh = highlight_list_arg(id, didh, LIST_STRING, 9101 0, sgp->sg_stop, "stop"); 9102 9103 didh = highlight_list_arg(id, didh, LIST_ATTR, 9104 sgp->sg_cterm, NULL, "cterm"); 9105 didh = highlight_list_arg(id, didh, LIST_INT, 9106 sgp->sg_cterm_fg, NULL, "ctermfg"); 9107 didh = highlight_list_arg(id, didh, LIST_INT, 9108 sgp->sg_cterm_bg, NULL, "ctermbg"); 9109 9110 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 9111 didh = highlight_list_arg(id, didh, LIST_ATTR, 9112 sgp->sg_gui, NULL, "gui"); 9113 didh = highlight_list_arg(id, didh, LIST_STRING, 9114 0, sgp->sg_gui_fg_name, "guifg"); 9115 didh = highlight_list_arg(id, didh, LIST_STRING, 9116 0, sgp->sg_gui_bg_name, "guibg"); 9117 didh = highlight_list_arg(id, didh, LIST_STRING, 9118 0, sgp->sg_gui_sp_name, "guisp"); 9119 #endif 9120 #ifdef FEAT_GUI 9121 didh = highlight_list_arg(id, didh, LIST_STRING, 9122 0, sgp->sg_font_name, "font"); 9123 #endif 9124 9125 if (sgp->sg_link && !got_int) 9126 { 9127 (void)syn_list_header(didh, 9999, id); 9128 didh = TRUE; 9129 msg_puts_attr((char_u *)"links to", HL_ATTR(HLF_D)); 9130 msg_putchar(' '); 9131 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name); 9132 } 9133 9134 if (!didh) 9135 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", ""); 9136 #ifdef FEAT_EVAL 9137 if (p_verbose > 0) 9138 last_set_msg(sgp->sg_scriptID); 9139 #endif 9140 } 9141 9142 static int 9143 highlight_list_arg( 9144 int id, 9145 int didh, 9146 int type, 9147 int iarg, 9148 char_u *sarg, 9149 char *name) 9150 { 9151 char_u buf[100]; 9152 char_u *ts; 9153 int i; 9154 9155 if (got_int) 9156 return FALSE; 9157 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0)) 9158 { 9159 ts = buf; 9160 if (type == LIST_INT) 9161 sprintf((char *)buf, "%d", iarg - 1); 9162 else if (type == LIST_STRING) 9163 ts = sarg; 9164 else /* type == LIST_ATTR */ 9165 { 9166 buf[0] = NUL; 9167 for (i = 0; hl_attr_table[i] != 0; ++i) 9168 { 9169 if (iarg & hl_attr_table[i]) 9170 { 9171 if (buf[0] != NUL) 9172 vim_strcat(buf, (char_u *)",", 100); 9173 vim_strcat(buf, (char_u *)hl_name_table[i], 100); 9174 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */ 9175 } 9176 } 9177 } 9178 9179 (void)syn_list_header(didh, 9180 (int)(vim_strsize(ts) + STRLEN(name) + 1), id); 9181 didh = TRUE; 9182 if (!got_int) 9183 { 9184 if (*name != NUL) 9185 { 9186 MSG_PUTS_ATTR(name, HL_ATTR(HLF_D)); 9187 MSG_PUTS_ATTR("=", HL_ATTR(HLF_D)); 9188 } 9189 msg_outtrans(ts); 9190 } 9191 } 9192 return didh; 9193 } 9194 9195 #if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO) 9196 /* 9197 * Return "1" if highlight group "id" has attribute "flag". 9198 * Return NULL otherwise. 9199 */ 9200 char_u * 9201 highlight_has_attr( 9202 int id, 9203 int flag, 9204 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */ 9205 { 9206 int attr; 9207 9208 if (id <= 0 || id > highlight_ga.ga_len) 9209 return NULL; 9210 9211 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 9212 if (modec == 'g') 9213 attr = HL_TABLE()[id - 1].sg_gui; 9214 else 9215 #endif 9216 if (modec == 'c') 9217 attr = HL_TABLE()[id - 1].sg_cterm; 9218 else 9219 attr = HL_TABLE()[id - 1].sg_term; 9220 9221 if (attr & flag) 9222 return (char_u *)"1"; 9223 return NULL; 9224 } 9225 #endif 9226 9227 #if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO) 9228 /* 9229 * Return color name of highlight group "id". 9230 */ 9231 char_u * 9232 highlight_color( 9233 int id, 9234 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */ 9235 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */ 9236 { 9237 static char_u name[20]; 9238 int n; 9239 int fg = FALSE; 9240 int sp = FALSE; 9241 int font = FALSE; 9242 9243 if (id <= 0 || id > highlight_ga.ga_len) 9244 return NULL; 9245 9246 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g') 9247 fg = TRUE; 9248 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o' 9249 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't') 9250 font = TRUE; 9251 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p') 9252 sp = TRUE; 9253 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) 9254 return NULL; 9255 if (modec == 'g') 9256 { 9257 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 9258 # ifdef FEAT_GUI 9259 /* return font name */ 9260 if (font) 9261 return HL_TABLE()[id - 1].sg_font_name; 9262 # endif 9263 9264 /* return #RRGGBB form (only possible when GUI is running) */ 9265 if ((USE_24BIT) && what[2] == '#') 9266 { 9267 guicolor_T color; 9268 long_u rgb; 9269 static char_u buf[10]; 9270 9271 if (fg) 9272 color = HL_TABLE()[id - 1].sg_gui_fg; 9273 else if (sp) 9274 # ifdef FEAT_GUI 9275 color = HL_TABLE()[id - 1].sg_gui_sp; 9276 # else 9277 color = INVALCOLOR; 9278 # endif 9279 else 9280 color = HL_TABLE()[id - 1].sg_gui_bg; 9281 if (color == INVALCOLOR) 9282 return NULL; 9283 rgb = (long_u)GUI_MCH_GET_RGB(color); 9284 sprintf((char *)buf, "#%02x%02x%02x", 9285 (unsigned)(rgb >> 16), 9286 (unsigned)(rgb >> 8) & 255, 9287 (unsigned)rgb & 255); 9288 return buf; 9289 } 9290 # endif 9291 if (fg) 9292 return (HL_TABLE()[id - 1].sg_gui_fg_name); 9293 if (sp) 9294 return (HL_TABLE()[id - 1].sg_gui_sp_name); 9295 return (HL_TABLE()[id - 1].sg_gui_bg_name); 9296 } 9297 if (font || sp) 9298 return NULL; 9299 if (modec == 'c') 9300 { 9301 if (fg) 9302 n = HL_TABLE()[id - 1].sg_cterm_fg - 1; 9303 else 9304 n = HL_TABLE()[id - 1].sg_cterm_bg - 1; 9305 if (n < 0) 9306 return NULL; 9307 sprintf((char *)name, "%d", n); 9308 return name; 9309 } 9310 /* term doesn't have color */ 9311 return NULL; 9312 } 9313 #endif 9314 9315 #if (defined(FEAT_SYN_HL) \ 9316 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \ 9317 && defined(FEAT_PRINTER)) || defined(PROTO) 9318 /* 9319 * Return color name of highlight group "id" as RGB value. 9320 */ 9321 long_u 9322 highlight_gui_color_rgb( 9323 int id, 9324 int fg) /* TRUE = fg, FALSE = bg */ 9325 { 9326 guicolor_T color; 9327 9328 if (id <= 0 || id > highlight_ga.ga_len) 9329 return 0L; 9330 9331 if (fg) 9332 color = HL_TABLE()[id - 1].sg_gui_fg; 9333 else 9334 color = HL_TABLE()[id - 1].sg_gui_bg; 9335 9336 if (color == INVALCOLOR) 9337 return 0L; 9338 9339 return GUI_MCH_GET_RGB(color); 9340 } 9341 #endif 9342 9343 /* 9344 * Output the syntax list header. 9345 * Return TRUE when started a new line. 9346 */ 9347 static int 9348 syn_list_header( 9349 int did_header, /* did header already */ 9350 int outlen, /* length of string that comes */ 9351 int id) /* highlight group id */ 9352 { 9353 int endcol = 19; 9354 int newline = TRUE; 9355 9356 if (!did_header) 9357 { 9358 msg_putchar('\n'); 9359 if (got_int) 9360 return TRUE; 9361 msg_outtrans(HL_TABLE()[id - 1].sg_name); 9362 endcol = 15; 9363 } 9364 else if (msg_col + outlen + 1 >= Columns) 9365 { 9366 msg_putchar('\n'); 9367 if (got_int) 9368 return TRUE; 9369 } 9370 else 9371 { 9372 if (msg_col >= endcol) /* wrap around is like starting a new line */ 9373 newline = FALSE; 9374 } 9375 9376 if (msg_col >= endcol) /* output at least one space */ 9377 endcol = msg_col + 1; 9378 if (Columns <= endcol) /* avoid hang for tiny window */ 9379 endcol = Columns - 1; 9380 9381 msg_advance(endcol); 9382 9383 /* Show "xxx" with the attributes. */ 9384 if (!did_header) 9385 { 9386 msg_puts_attr((char_u *)"xxx", syn_id2attr(id)); 9387 msg_putchar(' '); 9388 } 9389 9390 return newline; 9391 } 9392 9393 /* 9394 * Set the attribute numbers for a highlight group. 9395 * Called after one of the attributes has changed. 9396 */ 9397 static void 9398 set_hl_attr( 9399 int idx) /* index in array */ 9400 { 9401 attrentry_T at_en; 9402 struct hl_group *sgp = HL_TABLE() + idx; 9403 9404 /* The "Normal" group doesn't need an attribute number */ 9405 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0) 9406 return; 9407 9408 #ifdef FEAT_GUI 9409 /* 9410 * For the GUI mode: If there are other than "normal" highlighting 9411 * attributes, need to allocate an attr number. 9412 */ 9413 if (sgp->sg_gui_fg == INVALCOLOR 9414 && sgp->sg_gui_bg == INVALCOLOR 9415 && sgp->sg_gui_sp == INVALCOLOR 9416 && sgp->sg_font == NOFONT 9417 # ifdef FEAT_XFONTSET 9418 && sgp->sg_fontset == NOFONTSET 9419 # endif 9420 ) 9421 { 9422 sgp->sg_gui_attr = sgp->sg_gui; 9423 } 9424 else 9425 { 9426 at_en.ae_attr = sgp->sg_gui; 9427 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg; 9428 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg; 9429 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp; 9430 at_en.ae_u.gui.font = sgp->sg_font; 9431 # ifdef FEAT_XFONTSET 9432 at_en.ae_u.gui.fontset = sgp->sg_fontset; 9433 # endif 9434 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en); 9435 } 9436 #endif 9437 /* 9438 * For the term mode: If there are other than "normal" highlighting 9439 * attributes, need to allocate an attr number. 9440 */ 9441 if (sgp->sg_start == NULL && sgp->sg_stop == NULL) 9442 sgp->sg_term_attr = sgp->sg_term; 9443 else 9444 { 9445 at_en.ae_attr = sgp->sg_term; 9446 at_en.ae_u.term.start = sgp->sg_start; 9447 at_en.ae_u.term.stop = sgp->sg_stop; 9448 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en); 9449 } 9450 9451 /* 9452 * For the color term mode: If there are other than "normal" 9453 * highlighting attributes, need to allocate an attr number. 9454 */ 9455 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 9456 # ifdef FEAT_TERMGUICOLORS 9457 && sgp->sg_gui_fg == INVALCOLOR 9458 && sgp->sg_gui_bg == INVALCOLOR 9459 # endif 9460 ) 9461 sgp->sg_cterm_attr = sgp->sg_cterm; 9462 else 9463 { 9464 at_en.ae_attr = sgp->sg_cterm; 9465 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg; 9466 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg; 9467 # ifdef FEAT_TERMGUICOLORS 9468 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg); 9469 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg); 9470 # endif 9471 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en); 9472 } 9473 } 9474 9475 /* 9476 * Lookup a highlight group name and return its ID. 9477 * If it is not found, 0 is returned. 9478 */ 9479 int 9480 syn_name2id(char_u *name) 9481 { 9482 int i; 9483 char_u name_u[200]; 9484 9485 /* Avoid using stricmp() too much, it's slow on some systems */ 9486 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars 9487 * don't deserve to be found! */ 9488 vim_strncpy(name_u, name, 199); 9489 vim_strup(name_u); 9490 for (i = highlight_ga.ga_len; --i >= 0; ) 9491 if (HL_TABLE()[i].sg_name_u != NULL 9492 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0) 9493 break; 9494 return i + 1; 9495 } 9496 9497 #if defined(FEAT_EVAL) || defined(PROTO) 9498 /* 9499 * Return TRUE if highlight group "name" exists. 9500 */ 9501 int 9502 highlight_exists(char_u *name) 9503 { 9504 return (syn_name2id(name) > 0); 9505 } 9506 9507 # if defined(FEAT_SEARCH_EXTRA) || defined(PROTO) 9508 /* 9509 * Return the name of highlight group "id". 9510 * When not a valid ID return an empty string. 9511 */ 9512 char_u * 9513 syn_id2name(int id) 9514 { 9515 if (id <= 0 || id > highlight_ga.ga_len) 9516 return (char_u *)""; 9517 return HL_TABLE()[id - 1].sg_name; 9518 } 9519 # endif 9520 #endif 9521 9522 /* 9523 * Like syn_name2id(), but take a pointer + length argument. 9524 */ 9525 int 9526 syn_namen2id(char_u *linep, int len) 9527 { 9528 char_u *name; 9529 int id = 0; 9530 9531 name = vim_strnsave(linep, len); 9532 if (name != NULL) 9533 { 9534 id = syn_name2id(name); 9535 vim_free(name); 9536 } 9537 return id; 9538 } 9539 9540 /* 9541 * Find highlight group name in the table and return its ID. 9542 * The argument is a pointer to the name and the length of the name. 9543 * If it doesn't exist yet, a new entry is created. 9544 * Return 0 for failure. 9545 */ 9546 int 9547 syn_check_group(char_u *pp, int len) 9548 { 9549 int id; 9550 char_u *name; 9551 9552 name = vim_strnsave(pp, len); 9553 if (name == NULL) 9554 return 0; 9555 9556 id = syn_name2id(name); 9557 if (id == 0) /* doesn't exist yet */ 9558 id = syn_add_group(name); 9559 else 9560 vim_free(name); 9561 return id; 9562 } 9563 9564 /* 9565 * Add new highlight group and return its ID. 9566 * "name" must be an allocated string, it will be consumed. 9567 * Return 0 for failure. 9568 */ 9569 static int 9570 syn_add_group(char_u *name) 9571 { 9572 char_u *p; 9573 9574 /* Check that the name is ASCII letters, digits and underscore. */ 9575 for (p = name; *p != NUL; ++p) 9576 { 9577 if (!vim_isprintc(*p)) 9578 { 9579 EMSG(_("E669: Unprintable character in group name")); 9580 vim_free(name); 9581 return 0; 9582 } 9583 else if (!ASCII_ISALNUM(*p) && *p != '_') 9584 { 9585 /* This is an error, but since there previously was no check only 9586 * give a warning. */ 9587 msg_source(HL_ATTR(HLF_W)); 9588 MSG(_("W18: Invalid character in group name")); 9589 break; 9590 } 9591 } 9592 9593 /* 9594 * First call for this growarray: init growing array. 9595 */ 9596 if (highlight_ga.ga_data == NULL) 9597 { 9598 highlight_ga.ga_itemsize = sizeof(struct hl_group); 9599 highlight_ga.ga_growsize = 10; 9600 } 9601 9602 if (highlight_ga.ga_len >= MAX_HL_ID) 9603 { 9604 EMSG(_("E849: Too many highlight and syntax groups")); 9605 vim_free(name); 9606 return 0; 9607 } 9608 9609 /* 9610 * Make room for at least one other syntax_highlight entry. 9611 */ 9612 if (ga_grow(&highlight_ga, 1) == FAIL) 9613 { 9614 vim_free(name); 9615 return 0; 9616 } 9617 9618 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group)); 9619 HL_TABLE()[highlight_ga.ga_len].sg_name = name; 9620 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name); 9621 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 9622 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR; 9623 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR; 9624 # ifdef FEAT_GUI 9625 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR; 9626 # endif 9627 #endif 9628 ++highlight_ga.ga_len; 9629 9630 return highlight_ga.ga_len; /* ID is index plus one */ 9631 } 9632 9633 /* 9634 * When, just after calling syn_add_group(), an error is discovered, this 9635 * function deletes the new name. 9636 */ 9637 static void 9638 syn_unadd_group(void) 9639 { 9640 --highlight_ga.ga_len; 9641 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name); 9642 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u); 9643 } 9644 9645 /* 9646 * Translate a group ID to highlight attributes. 9647 */ 9648 int 9649 syn_id2attr(int hl_id) 9650 { 9651 int attr; 9652 struct hl_group *sgp; 9653 9654 hl_id = syn_get_final_id(hl_id); 9655 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */ 9656 9657 #ifdef FEAT_GUI 9658 /* 9659 * Only use GUI attr when the GUI is being used. 9660 */ 9661 if (gui.in_use) 9662 attr = sgp->sg_gui_attr; 9663 else 9664 #endif 9665 if (IS_CTERM) 9666 attr = sgp->sg_cterm_attr; 9667 else 9668 attr = sgp->sg_term_attr; 9669 9670 return attr; 9671 } 9672 9673 #ifdef FEAT_GUI 9674 /* 9675 * Get the GUI colors and attributes for a group ID. 9676 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise. 9677 */ 9678 int 9679 syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp) 9680 { 9681 struct hl_group *sgp; 9682 9683 hl_id = syn_get_final_id(hl_id); 9684 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */ 9685 9686 *fgp = sgp->sg_gui_fg; 9687 *bgp = sgp->sg_gui_bg; 9688 return sgp->sg_gui; 9689 } 9690 #endif 9691 9692 /* 9693 * Translate a group ID to the final group ID (following links). 9694 */ 9695 int 9696 syn_get_final_id(int hl_id) 9697 { 9698 int count; 9699 struct hl_group *sgp; 9700 9701 if (hl_id > highlight_ga.ga_len || hl_id < 1) 9702 return 0; /* Can be called from eval!! */ 9703 9704 /* 9705 * Follow links until there is no more. 9706 * Look out for loops! Break after 100 links. 9707 */ 9708 for (count = 100; --count >= 0; ) 9709 { 9710 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */ 9711 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len) 9712 break; 9713 hl_id = sgp->sg_link; 9714 } 9715 9716 return hl_id; 9717 } 9718 9719 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 9720 /* 9721 * Call this function just after the GUI has started. 9722 * It finds the font and color handles for the highlighting groups. 9723 */ 9724 void 9725 highlight_gui_started(void) 9726 { 9727 int idx; 9728 9729 /* First get the colors from the "Normal" and "Menu" group, if set */ 9730 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) 9731 # ifdef FEAT_TERMGUICOLORS 9732 if (USE_24BIT) 9733 # endif 9734 set_normal_colors(); 9735 # endif 9736 9737 for (idx = 0; idx < highlight_ga.ga_len; ++idx) 9738 gui_do_one_color(idx, FALSE, FALSE); 9739 9740 highlight_changed(); 9741 } 9742 9743 static void 9744 gui_do_one_color( 9745 int idx, 9746 int do_menu UNUSED, /* TRUE: might set the menu font */ 9747 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */ 9748 { 9749 int didit = FALSE; 9750 9751 # ifdef FEAT_GUI 9752 # ifdef FEAT_TERMGUICOLORS 9753 if (gui.in_use) 9754 # endif 9755 if (HL_TABLE()[idx].sg_font_name != NULL) 9756 { 9757 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu, 9758 do_tooltip, TRUE); 9759 didit = TRUE; 9760 } 9761 # endif 9762 if (HL_TABLE()[idx].sg_gui_fg_name != NULL) 9763 { 9764 HL_TABLE()[idx].sg_gui_fg = 9765 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name); 9766 didit = TRUE; 9767 } 9768 if (HL_TABLE()[idx].sg_gui_bg_name != NULL) 9769 { 9770 HL_TABLE()[idx].sg_gui_bg = 9771 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name); 9772 didit = TRUE; 9773 } 9774 # ifdef FEAT_GUI 9775 if (HL_TABLE()[idx].sg_gui_sp_name != NULL) 9776 { 9777 HL_TABLE()[idx].sg_gui_sp = 9778 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name); 9779 didit = TRUE; 9780 } 9781 # endif 9782 if (didit) /* need to get a new attr number */ 9783 set_hl_attr(idx); 9784 } 9785 #endif 9786 9787 /* 9788 * Translate the 'highlight' option into attributes in highlight_attr[] and 9789 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of 9790 * corresponding highlights to use on top of HLF_SNC is computed. 9791 * Called only when the 'highlight' option has been changed and upon first 9792 * screen redraw after any :highlight command. 9793 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise. 9794 */ 9795 int 9796 highlight_changed(void) 9797 { 9798 int hlf; 9799 int i; 9800 char_u *p; 9801 int attr; 9802 char_u *end; 9803 int id; 9804 #ifdef USER_HIGHLIGHT 9805 char_u userhl[10]; 9806 # ifdef FEAT_STL_OPT 9807 int id_SNC = -1; 9808 int id_S = -1; 9809 int hlcnt; 9810 # endif 9811 #endif 9812 static int hl_flags[HLF_COUNT] = HL_FLAGS; 9813 9814 need_highlight_changed = FALSE; 9815 9816 /* 9817 * Clear all attributes. 9818 */ 9819 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf) 9820 highlight_attr[hlf] = 0; 9821 9822 /* 9823 * First set all attributes to their default value. 9824 * Then use the attributes from the 'highlight' option. 9825 */ 9826 for (i = 0; i < 2; ++i) 9827 { 9828 if (i) 9829 p = p_hl; 9830 else 9831 p = get_highlight_default(); 9832 if (p == NULL) /* just in case */ 9833 continue; 9834 9835 while (*p) 9836 { 9837 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf) 9838 if (hl_flags[hlf] == *p) 9839 break; 9840 ++p; 9841 if (hlf == (int)HLF_COUNT || *p == NUL) 9842 return FAIL; 9843 9844 /* 9845 * Allow several hl_flags to be combined, like "bu" for 9846 * bold-underlined. 9847 */ 9848 attr = 0; 9849 for ( ; *p && *p != ','; ++p) /* parse upto comma */ 9850 { 9851 if (VIM_ISWHITE(*p)) /* ignore white space */ 9852 continue; 9853 9854 if (attr > HL_ALL) /* Combination with ':' is not allowed. */ 9855 return FAIL; 9856 9857 switch (*p) 9858 { 9859 case 'b': attr |= HL_BOLD; 9860 break; 9861 case 'i': attr |= HL_ITALIC; 9862 break; 9863 case '-': 9864 case 'n': /* no highlighting */ 9865 break; 9866 case 'r': attr |= HL_INVERSE; 9867 break; 9868 case 's': attr |= HL_STANDOUT; 9869 break; 9870 case 'u': attr |= HL_UNDERLINE; 9871 break; 9872 case 'c': attr |= HL_UNDERCURL; 9873 break; 9874 case ':': ++p; /* highlight group name */ 9875 if (attr || *p == NUL) /* no combinations */ 9876 return FAIL; 9877 end = vim_strchr(p, ','); 9878 if (end == NULL) 9879 end = p + STRLEN(p); 9880 id = syn_check_group(p, (int)(end - p)); 9881 if (id == 0) 9882 return FAIL; 9883 attr = syn_id2attr(id); 9884 p = end - 1; 9885 #if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT) 9886 if (hlf == (int)HLF_SNC) 9887 id_SNC = syn_get_final_id(id); 9888 else if (hlf == (int)HLF_S) 9889 id_S = syn_get_final_id(id); 9890 #endif 9891 break; 9892 default: return FAIL; 9893 } 9894 } 9895 highlight_attr[hlf] = attr; 9896 9897 p = skip_to_option_part(p); /* skip comma and spaces */ 9898 } 9899 } 9900 9901 #ifdef USER_HIGHLIGHT 9902 /* Setup the user highlights 9903 * 9904 * Temporarily utilize 10 more hl entries. Have to be in there 9905 * simultaneously in case of table overflows in get_attr_entry() 9906 */ 9907 # ifdef FEAT_STL_OPT 9908 if (ga_grow(&highlight_ga, 10) == FAIL) 9909 return FAIL; 9910 hlcnt = highlight_ga.ga_len; 9911 if (id_S == 0) 9912 { /* Make sure id_S is always valid to simplify code below */ 9913 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group)); 9914 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S]; 9915 id_S = hlcnt + 10; 9916 } 9917 # endif 9918 for (i = 0; i < 9; i++) 9919 { 9920 sprintf((char *)userhl, "User%d", i + 1); 9921 id = syn_name2id(userhl); 9922 if (id == 0) 9923 { 9924 highlight_user[i] = 0; 9925 # ifdef FEAT_STL_OPT 9926 highlight_stlnc[i] = 0; 9927 # endif 9928 } 9929 else 9930 { 9931 # ifdef FEAT_STL_OPT 9932 struct hl_group *hlt = HL_TABLE(); 9933 # endif 9934 9935 highlight_user[i] = syn_id2attr(id); 9936 # ifdef FEAT_STL_OPT 9937 if (id_SNC == 0) 9938 { 9939 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group)); 9940 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC]; 9941 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC]; 9942 # if defined(FEAT_GUI) || defined(FEAT_EVAL) 9943 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC]; 9944 # endif 9945 } 9946 else 9947 mch_memmove(&hlt[hlcnt + i], 9948 &hlt[id_SNC - 1], 9949 sizeof(struct hl_group)); 9950 hlt[hlcnt + i].sg_link = 0; 9951 9952 /* Apply difference between UserX and HLF_S to HLF_SNC */ 9953 hlt[hlcnt + i].sg_term ^= 9954 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term; 9955 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start) 9956 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start; 9957 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop) 9958 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop; 9959 hlt[hlcnt + i].sg_cterm ^= 9960 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm; 9961 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg) 9962 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg; 9963 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg) 9964 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg; 9965 # if defined(FEAT_GUI) || defined(FEAT_EVAL) 9966 hlt[hlcnt + i].sg_gui ^= 9967 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui; 9968 # endif 9969 # ifdef FEAT_GUI 9970 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg) 9971 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg; 9972 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg) 9973 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg; 9974 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp) 9975 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp; 9976 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font) 9977 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font; 9978 # ifdef FEAT_XFONTSET 9979 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset) 9980 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset; 9981 # endif 9982 # endif 9983 highlight_ga.ga_len = hlcnt + i + 1; 9984 set_hl_attr(hlcnt + i); /* At long last we can apply */ 9985 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1); 9986 # endif 9987 } 9988 } 9989 # ifdef FEAT_STL_OPT 9990 highlight_ga.ga_len = hlcnt; 9991 # endif 9992 9993 #endif /* USER_HIGHLIGHT */ 9994 9995 return OK; 9996 } 9997 9998 #if defined(FEAT_CMDL_COMPL) || defined(PROTO) 9999 10000 static void highlight_list(void); 10001 static void highlight_list_two(int cnt, int attr); 10002 10003 /* 10004 * Handle command line completion for :highlight command. 10005 */ 10006 void 10007 set_context_in_highlight_cmd(expand_T *xp, char_u *arg) 10008 { 10009 char_u *p; 10010 10011 /* Default: expand group names */ 10012 xp->xp_context = EXPAND_HIGHLIGHT; 10013 xp->xp_pattern = arg; 10014 include_link = 2; 10015 include_default = 1; 10016 10017 /* (part of) subcommand already typed */ 10018 if (*arg != NUL) 10019 { 10020 p = skiptowhite(arg); 10021 if (*p != NUL) /* past "default" or group name */ 10022 { 10023 include_default = 0; 10024 if (STRNCMP("default", arg, p - arg) == 0) 10025 { 10026 arg = skipwhite(p); 10027 xp->xp_pattern = arg; 10028 p = skiptowhite(arg); 10029 } 10030 if (*p != NUL) /* past group name */ 10031 { 10032 include_link = 0; 10033 if (arg[1] == 'i' && arg[0] == 'N') 10034 highlight_list(); 10035 if (STRNCMP("link", arg, p - arg) == 0 10036 || STRNCMP("clear", arg, p - arg) == 0) 10037 { 10038 xp->xp_pattern = skipwhite(p); 10039 p = skiptowhite(xp->xp_pattern); 10040 if (*p != NUL) /* past first group name */ 10041 { 10042 xp->xp_pattern = skipwhite(p); 10043 p = skiptowhite(xp->xp_pattern); 10044 } 10045 } 10046 if (*p != NUL) /* past group name(s) */ 10047 xp->xp_context = EXPAND_NOTHING; 10048 } 10049 } 10050 } 10051 } 10052 10053 /* 10054 * List highlighting matches in a nice way. 10055 */ 10056 static void 10057 highlight_list(void) 10058 { 10059 int i; 10060 10061 for (i = 10; --i >= 0; ) 10062 highlight_list_two(i, HL_ATTR(HLF_D)); 10063 for (i = 40; --i >= 0; ) 10064 highlight_list_two(99, 0); 10065 } 10066 10067 static void 10068 highlight_list_two(int cnt, int attr) 10069 { 10070 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr); 10071 msg_clr_eos(); 10072 out_flush(); 10073 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE); 10074 } 10075 10076 #endif /* FEAT_CMDL_COMPL */ 10077 10078 #if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \ 10079 || defined(FEAT_SIGNS) || defined(PROTO) 10080 /* 10081 * Function given to ExpandGeneric() to obtain the list of group names. 10082 */ 10083 char_u * 10084 get_highlight_name(expand_T *xp UNUSED, int idx) 10085 { 10086 return get_highlight_name_ext(xp, idx, TRUE); 10087 } 10088 10089 /* 10090 * Obtain a highlight group name. 10091 * When "skip_cleared" is TRUE don't return a cleared entry. 10092 */ 10093 char_u * 10094 get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared) 10095 { 10096 if (idx < 0) 10097 return NULL; 10098 10099 /* Items are never removed from the table, skip the ones that were 10100 * cleared. */ 10101 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared) 10102 return (char_u *)""; 10103 10104 #ifdef FEAT_CMDL_COMPL 10105 if (idx == highlight_ga.ga_len && include_none != 0) 10106 return (char_u *)"none"; 10107 if (idx == highlight_ga.ga_len + include_none && include_default != 0) 10108 return (char_u *)"default"; 10109 if (idx == highlight_ga.ga_len + include_none + include_default 10110 && include_link != 0) 10111 return (char_u *)"link"; 10112 if (idx == highlight_ga.ga_len + include_none + include_default + 1 10113 && include_link != 0) 10114 return (char_u *)"clear"; 10115 #endif 10116 if (idx >= highlight_ga.ga_len) 10117 return NULL; 10118 return HL_TABLE()[idx].sg_name; 10119 } 10120 #endif 10121 10122 #if defined(FEAT_GUI) || defined(PROTO) 10123 /* 10124 * Free all the highlight group fonts. 10125 * Used when quitting for systems which need it. 10126 */ 10127 void 10128 free_highlight_fonts(void) 10129 { 10130 int idx; 10131 10132 for (idx = 0; idx < highlight_ga.ga_len; ++idx) 10133 { 10134 gui_mch_free_font(HL_TABLE()[idx].sg_font); 10135 HL_TABLE()[idx].sg_font = NOFONT; 10136 # ifdef FEAT_XFONTSET 10137 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset); 10138 HL_TABLE()[idx].sg_fontset = NOFONTSET; 10139 # endif 10140 } 10141 10142 gui_mch_free_font(gui.norm_font); 10143 # ifdef FEAT_XFONTSET 10144 gui_mch_free_fontset(gui.fontset); 10145 # endif 10146 # ifndef FEAT_GUI_GTK 10147 gui_mch_free_font(gui.bold_font); 10148 gui_mch_free_font(gui.ital_font); 10149 gui_mch_free_font(gui.boldital_font); 10150 # endif 10151 } 10152 #endif 10153 10154 /************************************** 10155 * End of Highlighting stuff * 10156 **************************************/ 10157