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