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