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