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