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