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