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