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