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