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 { 3473 EMSG2(_("E390: Illegal argument: %s"), arg); 3474 return; 3475 } 3476 3477 /* assume spell checking changed, force a redraw */ 3478 redraw_win_later(curwin, NOT_VALID); 3479 } 3480 3481 /* 3482 * Clear all syntax info for one buffer. 3483 */ 3484 void 3485 syntax_clear(block) 3486 synblock_T *block; 3487 { 3488 int i; 3489 3490 block->b_syn_error = FALSE; /* clear previous error */ 3491 block->b_syn_ic = FALSE; /* Use case, by default */ 3492 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */ 3493 block->b_syn_containedin = FALSE; 3494 3495 /* free the keywords */ 3496 clear_keywtab(&block->b_keywtab); 3497 clear_keywtab(&block->b_keywtab_ic); 3498 3499 /* free the syntax patterns */ 3500 for (i = block->b_syn_patterns.ga_len; --i >= 0; ) 3501 syn_clear_pattern(block, i); 3502 ga_clear(&block->b_syn_patterns); 3503 3504 /* free the syntax clusters */ 3505 for (i = block->b_syn_clusters.ga_len; --i >= 0; ) 3506 syn_clear_cluster(block, i); 3507 ga_clear(&block->b_syn_clusters); 3508 block->b_spell_cluster_id = 0; 3509 block->b_nospell_cluster_id = 0; 3510 3511 block->b_syn_sync_flags = 0; 3512 block->b_syn_sync_minlines = 0; 3513 block->b_syn_sync_maxlines = 0; 3514 block->b_syn_sync_linebreaks = 0; 3515 3516 vim_regfree(block->b_syn_linecont_prog); 3517 block->b_syn_linecont_prog = NULL; 3518 vim_free(block->b_syn_linecont_pat); 3519 block->b_syn_linecont_pat = NULL; 3520 #ifdef FEAT_FOLDING 3521 block->b_syn_folditems = 0; 3522 #endif 3523 3524 /* free the stored states */ 3525 syn_stack_free_all(block); 3526 invalidate_current_state(); 3527 3528 /* Reset the counter for ":syn include" */ 3529 running_syn_inc_tag = 0; 3530 } 3531 3532 /* 3533 * Get rid of ownsyntax for window "wp". 3534 */ 3535 void 3536 reset_synblock(wp) 3537 win_T *wp; 3538 { 3539 if (wp->w_s != &wp->w_buffer->b_s) 3540 { 3541 syntax_clear(wp->w_s); 3542 vim_free(wp->w_s); 3543 wp->w_s = &wp->w_buffer->b_s; 3544 } 3545 } 3546 3547 /* 3548 * Clear syncing info for one buffer. 3549 */ 3550 static void 3551 syntax_sync_clear() 3552 { 3553 int i; 3554 3555 /* free the syntax patterns */ 3556 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; ) 3557 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing) 3558 syn_remove_pattern(curwin->w_s, i); 3559 3560 curwin->w_s->b_syn_sync_flags = 0; 3561 curwin->w_s->b_syn_sync_minlines = 0; 3562 curwin->w_s->b_syn_sync_maxlines = 0; 3563 curwin->w_s->b_syn_sync_linebreaks = 0; 3564 3565 vim_regfree(curwin->w_s->b_syn_linecont_prog); 3566 curwin->w_s->b_syn_linecont_prog = NULL; 3567 vim_free(curwin->w_s->b_syn_linecont_pat); 3568 curwin->w_s->b_syn_linecont_pat = NULL; 3569 3570 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ 3571 } 3572 3573 /* 3574 * Remove one pattern from the buffer's pattern list. 3575 */ 3576 static void 3577 syn_remove_pattern(block, idx) 3578 synblock_T *block; 3579 int idx; 3580 { 3581 synpat_T *spp; 3582 3583 spp = &(SYN_ITEMS(block)[idx]); 3584 #ifdef FEAT_FOLDING 3585 if (spp->sp_flags & HL_FOLD) 3586 --block->b_syn_folditems; 3587 #endif 3588 syn_clear_pattern(block, idx); 3589 mch_memmove(spp, spp + 1, 3590 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1)); 3591 --block->b_syn_patterns.ga_len; 3592 } 3593 3594 /* 3595 * Clear and free one syntax pattern. When clearing all, must be called from 3596 * last to first! 3597 */ 3598 static void 3599 syn_clear_pattern(block, i) 3600 synblock_T *block; 3601 int i; 3602 { 3603 vim_free(SYN_ITEMS(block)[i].sp_pattern); 3604 vim_regfree(SYN_ITEMS(block)[i].sp_prog); 3605 /* Only free sp_cont_list and sp_next_list of first start pattern */ 3606 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START) 3607 { 3608 vim_free(SYN_ITEMS(block)[i].sp_cont_list); 3609 vim_free(SYN_ITEMS(block)[i].sp_next_list); 3610 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list); 3611 } 3612 } 3613 3614 /* 3615 * Clear and free one syntax cluster. 3616 */ 3617 static void 3618 syn_clear_cluster(block, i) 3619 synblock_T *block; 3620 int i; 3621 { 3622 vim_free(SYN_CLSTR(block)[i].scl_name); 3623 vim_free(SYN_CLSTR(block)[i].scl_name_u); 3624 vim_free(SYN_CLSTR(block)[i].scl_list); 3625 } 3626 3627 /* 3628 * Handle ":syntax clear" command. 3629 */ 3630 static void 3631 syn_cmd_clear(eap, syncing) 3632 exarg_T *eap; 3633 int syncing; 3634 { 3635 char_u *arg = eap->arg; 3636 char_u *arg_end; 3637 int id; 3638 3639 eap->nextcmd = find_nextcmd(arg); 3640 if (eap->skip) 3641 return; 3642 3643 /* 3644 * We have to disable this within ":syn include @group filename", 3645 * because otherwise @group would get deleted. 3646 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn 3647 * clear". 3648 */ 3649 if (curwin->w_s->b_syn_topgrp != 0) 3650 return; 3651 3652 if (ends_excmd(*arg)) 3653 { 3654 /* 3655 * No argument: Clear all syntax items. 3656 */ 3657 if (syncing) 3658 syntax_sync_clear(); 3659 else 3660 { 3661 syntax_clear(curwin->w_s); 3662 if (curwin->w_s == &curwin->w_buffer->b_s) 3663 do_unlet((char_u *)"b:current_syntax", TRUE); 3664 do_unlet((char_u *)"w:current_syntax", TRUE); 3665 } 3666 } 3667 else 3668 { 3669 /* 3670 * Clear the group IDs that are in the argument. 3671 */ 3672 while (!ends_excmd(*arg)) 3673 { 3674 arg_end = skiptowhite(arg); 3675 if (*arg == '@') 3676 { 3677 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1)); 3678 if (id == 0) 3679 { 3680 EMSG2(_("E391: No such syntax cluster: %s"), arg); 3681 break; 3682 } 3683 else 3684 { 3685 /* 3686 * We can't physically delete a cluster without changing 3687 * the IDs of other clusters, so we do the next best thing 3688 * and make it empty. 3689 */ 3690 short scl_id = id - SYNID_CLUSTER; 3691 3692 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list); 3693 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL; 3694 } 3695 } 3696 else 3697 { 3698 id = syn_namen2id(arg, (int)(arg_end - arg)); 3699 if (id == 0) 3700 { 3701 EMSG2(_(e_nogroup), arg); 3702 break; 3703 } 3704 else 3705 syn_clear_one(id, syncing); 3706 } 3707 arg = skipwhite(arg_end); 3708 } 3709 } 3710 redraw_curbuf_later(SOME_VALID); 3711 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ 3712 } 3713 3714 /* 3715 * Clear one syntax group for the current buffer. 3716 */ 3717 static void 3718 syn_clear_one(id, syncing) 3719 int id; 3720 int syncing; 3721 { 3722 synpat_T *spp; 3723 int idx; 3724 3725 /* Clear keywords only when not ":syn sync clear group-name" */ 3726 if (!syncing) 3727 { 3728 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab); 3729 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic); 3730 } 3731 3732 /* clear the patterns for "id" */ 3733 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; ) 3734 { 3735 spp = &(SYN_ITEMS(curwin->w_s)[idx]); 3736 if (spp->sp_syn.id != id || spp->sp_syncing != syncing) 3737 continue; 3738 syn_remove_pattern(curwin->w_s, idx); 3739 } 3740 } 3741 3742 /* 3743 * Handle ":syntax on" command. 3744 */ 3745 static void 3746 syn_cmd_on(eap, syncing) 3747 exarg_T *eap; 3748 int syncing UNUSED; 3749 { 3750 syn_cmd_onoff(eap, "syntax"); 3751 } 3752 3753 /* 3754 * Handle ":syntax enable" command. 3755 */ 3756 static void 3757 syn_cmd_enable(eap, syncing) 3758 exarg_T *eap; 3759 int syncing UNUSED; 3760 { 3761 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable"); 3762 syn_cmd_onoff(eap, "syntax"); 3763 do_unlet((char_u *)"g:syntax_cmd", TRUE); 3764 } 3765 3766 /* 3767 * Handle ":syntax reset" command. 3768 */ 3769 static void 3770 syn_cmd_reset(eap, syncing) 3771 exarg_T *eap; 3772 int syncing UNUSED; 3773 { 3774 eap->nextcmd = check_nextcmd(eap->arg); 3775 if (!eap->skip) 3776 { 3777 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset"); 3778 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim"); 3779 do_unlet((char_u *)"g:syntax_cmd", TRUE); 3780 } 3781 } 3782 3783 /* 3784 * Handle ":syntax manual" command. 3785 */ 3786 static void 3787 syn_cmd_manual(eap, syncing) 3788 exarg_T *eap; 3789 int syncing UNUSED; 3790 { 3791 syn_cmd_onoff(eap, "manual"); 3792 } 3793 3794 /* 3795 * Handle ":syntax off" command. 3796 */ 3797 static void 3798 syn_cmd_off(eap, syncing) 3799 exarg_T *eap; 3800 int syncing UNUSED; 3801 { 3802 syn_cmd_onoff(eap, "nosyntax"); 3803 } 3804 3805 static void 3806 syn_cmd_onoff(eap, name) 3807 exarg_T *eap; 3808 char *name; 3809 { 3810 char_u buf[100]; 3811 3812 eap->nextcmd = check_nextcmd(eap->arg); 3813 if (!eap->skip) 3814 { 3815 STRCPY(buf, "so "); 3816 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name); 3817 do_cmdline_cmd(buf); 3818 } 3819 } 3820 3821 /* 3822 * Handle ":syntax [list]" command: list current syntax words. 3823 */ 3824 static void 3825 syn_cmd_list(eap, syncing) 3826 exarg_T *eap; 3827 int syncing; /* when TRUE: list syncing items */ 3828 { 3829 char_u *arg = eap->arg; 3830 int id; 3831 char_u *arg_end; 3832 3833 eap->nextcmd = find_nextcmd(arg); 3834 if (eap->skip) 3835 return; 3836 3837 if (!syntax_present(curwin)) 3838 { 3839 MSG(_(msg_no_items)); 3840 return; 3841 } 3842 3843 if (syncing) 3844 { 3845 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT) 3846 { 3847 MSG_PUTS(_("syncing on C-style comments")); 3848 syn_lines_msg(); 3849 syn_match_msg(); 3850 return; 3851 } 3852 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH)) 3853 { 3854 if (curwin->w_s->b_syn_sync_minlines == 0) 3855 MSG_PUTS(_("no syncing")); 3856 else 3857 { 3858 MSG_PUTS(_("syncing starts ")); 3859 msg_outnum(curwin->w_s->b_syn_sync_minlines); 3860 MSG_PUTS(_(" lines before top line")); 3861 syn_match_msg(); 3862 } 3863 return; 3864 } 3865 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---")); 3866 if (curwin->w_s->b_syn_sync_minlines > 0 3867 || curwin->w_s->b_syn_sync_maxlines > 0 3868 || curwin->w_s->b_syn_sync_linebreaks > 0) 3869 { 3870 MSG_PUTS(_("\nsyncing on items")); 3871 syn_lines_msg(); 3872 syn_match_msg(); 3873 } 3874 } 3875 else 3876 MSG_PUTS_TITLE(_("\n--- Syntax items ---")); 3877 if (ends_excmd(*arg)) 3878 { 3879 /* 3880 * No argument: List all group IDs and all syntax clusters. 3881 */ 3882 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id) 3883 syn_list_one(id, syncing, FALSE); 3884 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id) 3885 syn_list_cluster(id); 3886 } 3887 else 3888 { 3889 /* 3890 * List the group IDs and syntax clusters that are in the argument. 3891 */ 3892 while (!ends_excmd(*arg) && !got_int) 3893 { 3894 arg_end = skiptowhite(arg); 3895 if (*arg == '@') 3896 { 3897 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1)); 3898 if (id == 0) 3899 EMSG2(_("E392: No such syntax cluster: %s"), arg); 3900 else 3901 syn_list_cluster(id - SYNID_CLUSTER); 3902 } 3903 else 3904 { 3905 id = syn_namen2id(arg, (int)(arg_end - arg)); 3906 if (id == 0) 3907 EMSG2(_(e_nogroup), arg); 3908 else 3909 syn_list_one(id, syncing, TRUE); 3910 } 3911 arg = skipwhite(arg_end); 3912 } 3913 } 3914 eap->nextcmd = check_nextcmd(arg); 3915 } 3916 3917 static void 3918 syn_lines_msg() 3919 { 3920 if (curwin->w_s->b_syn_sync_maxlines > 0 3921 || curwin->w_s->b_syn_sync_minlines > 0) 3922 { 3923 MSG_PUTS("; "); 3924 if (curwin->w_s->b_syn_sync_minlines > 0) 3925 { 3926 MSG_PUTS(_("minimal ")); 3927 msg_outnum(curwin->w_s->b_syn_sync_minlines); 3928 if (curwin->w_s->b_syn_sync_maxlines) 3929 MSG_PUTS(", "); 3930 } 3931 if (curwin->w_s->b_syn_sync_maxlines > 0) 3932 { 3933 MSG_PUTS(_("maximal ")); 3934 msg_outnum(curwin->w_s->b_syn_sync_maxlines); 3935 } 3936 MSG_PUTS(_(" lines before top line")); 3937 } 3938 } 3939 3940 static void 3941 syn_match_msg() 3942 { 3943 if (curwin->w_s->b_syn_sync_linebreaks > 0) 3944 { 3945 MSG_PUTS(_("; match ")); 3946 msg_outnum(curwin->w_s->b_syn_sync_linebreaks); 3947 MSG_PUTS(_(" line breaks")); 3948 } 3949 } 3950 3951 static int last_matchgroup; 3952 3953 struct name_list 3954 { 3955 int flag; 3956 char *name; 3957 }; 3958 3959 static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr)); 3960 3961 /* 3962 * List one syntax item, for ":syntax" or "syntax list syntax_name". 3963 */ 3964 static void 3965 syn_list_one(id, syncing, link_only) 3966 int id; 3967 int syncing; /* when TRUE: list syncing items */ 3968 int link_only; /* when TRUE; list link-only too */ 3969 { 3970 int attr; 3971 int idx; 3972 int did_header = FALSE; 3973 synpat_T *spp; 3974 static struct name_list namelist1[] = 3975 { 3976 {HL_DISPLAY, "display"}, 3977 {HL_CONTAINED, "contained"}, 3978 {HL_ONELINE, "oneline"}, 3979 {HL_KEEPEND, "keepend"}, 3980 {HL_EXTEND, "extend"}, 3981 {HL_EXCLUDENL, "excludenl"}, 3982 {HL_TRANSP, "transparent"}, 3983 {HL_FOLD, "fold"}, 3984 #ifdef FEAT_CONCEAL 3985 {HL_CONCEAL, "conceal"}, 3986 {HL_CONCEALENDS, "concealends"}, 3987 #endif 3988 {0, NULL} 3989 }; 3990 static struct name_list namelist2[] = 3991 { 3992 {HL_SKIPWHITE, "skipwhite"}, 3993 {HL_SKIPNL, "skipnl"}, 3994 {HL_SKIPEMPTY, "skipempty"}, 3995 {0, NULL} 3996 }; 3997 3998 attr = hl_attr(HLF_D); /* highlight like directories */ 3999 4000 /* list the keywords for "id" */ 4001 if (!syncing) 4002 { 4003 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr); 4004 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic, 4005 did_header, attr); 4006 } 4007 4008 /* list the patterns for "id" */ 4009 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx) 4010 { 4011 spp = &(SYN_ITEMS(curwin->w_s)[idx]); 4012 if (spp->sp_syn.id != id || spp->sp_syncing != syncing) 4013 continue; 4014 4015 (void)syn_list_header(did_header, 999, id); 4016 did_header = TRUE; 4017 last_matchgroup = 0; 4018 if (spp->sp_type == SPTYPE_MATCH) 4019 { 4020 put_pattern("match", ' ', spp, attr); 4021 msg_putchar(' '); 4022 } 4023 else if (spp->sp_type == SPTYPE_START) 4024 { 4025 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START) 4026 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr); 4027 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP) 4028 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr); 4029 while (idx < curwin->w_s->b_syn_patterns.ga_len 4030 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END) 4031 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr); 4032 --idx; 4033 msg_putchar(' '); 4034 } 4035 syn_list_flags(namelist1, spp->sp_flags, attr); 4036 4037 if (spp->sp_cont_list != NULL) 4038 put_id_list((char_u *)"contains", spp->sp_cont_list, attr); 4039 4040 if (spp->sp_syn.cont_in_list != NULL) 4041 put_id_list((char_u *)"containedin", 4042 spp->sp_syn.cont_in_list, attr); 4043 4044 if (spp->sp_next_list != NULL) 4045 { 4046 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr); 4047 syn_list_flags(namelist2, spp->sp_flags, attr); 4048 } 4049 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE)) 4050 { 4051 if (spp->sp_flags & HL_SYNC_HERE) 4052 msg_puts_attr((char_u *)"grouphere", attr); 4053 else 4054 msg_puts_attr((char_u *)"groupthere", attr); 4055 msg_putchar(' '); 4056 if (spp->sp_sync_idx >= 0) 4057 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s) 4058 [spp->sp_sync_idx].sp_syn.id - 1].sg_name); 4059 else 4060 MSG_PUTS("NONE"); 4061 msg_putchar(' '); 4062 } 4063 } 4064 4065 /* list the link, if there is one */ 4066 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int) 4067 { 4068 (void)syn_list_header(did_header, 999, id); 4069 msg_puts_attr((char_u *)"links to", attr); 4070 msg_putchar(' '); 4071 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name); 4072 } 4073 } 4074 4075 static void 4076 syn_list_flags(nlist, flags, attr) 4077 struct name_list *nlist; 4078 int flags; 4079 int attr; 4080 { 4081 int i; 4082 4083 for (i = 0; nlist[i].flag != 0; ++i) 4084 if (flags & nlist[i].flag) 4085 { 4086 msg_puts_attr((char_u *)nlist[i].name, attr); 4087 msg_putchar(' '); 4088 } 4089 } 4090 4091 /* 4092 * List one syntax cluster, for ":syntax" or "syntax list syntax_name". 4093 */ 4094 static void 4095 syn_list_cluster(id) 4096 int id; 4097 { 4098 int endcol = 15; 4099 4100 /* slight hack: roughly duplicate the guts of syn_list_header() */ 4101 msg_putchar('\n'); 4102 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name); 4103 4104 if (msg_col >= endcol) /* output at least one space */ 4105 endcol = msg_col + 1; 4106 if (Columns <= endcol) /* avoid hang for tiny window */ 4107 endcol = Columns - 1; 4108 4109 msg_advance(endcol); 4110 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL) 4111 { 4112 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list, 4113 hl_attr(HLF_D)); 4114 } 4115 else 4116 { 4117 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D)); 4118 msg_puts((char_u *)"=NONE"); 4119 } 4120 } 4121 4122 static void 4123 put_id_list(name, list, attr) 4124 char_u *name; 4125 short *list; 4126 int attr; 4127 { 4128 short *p; 4129 4130 msg_puts_attr(name, attr); 4131 msg_putchar('='); 4132 for (p = list; *p; ++p) 4133 { 4134 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP) 4135 { 4136 if (p[1]) 4137 MSG_PUTS("ALLBUT"); 4138 else 4139 MSG_PUTS("ALL"); 4140 } 4141 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED) 4142 { 4143 MSG_PUTS("TOP"); 4144 } 4145 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER) 4146 { 4147 MSG_PUTS("CONTAINED"); 4148 } 4149 else if (*p >= SYNID_CLUSTER) 4150 { 4151 short scl_id = *p - SYNID_CLUSTER; 4152 4153 msg_putchar('@'); 4154 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name); 4155 } 4156 else 4157 msg_outtrans(HL_TABLE()[*p - 1].sg_name); 4158 if (p[1]) 4159 msg_putchar(','); 4160 } 4161 msg_putchar(' '); 4162 } 4163 4164 static void 4165 put_pattern(s, c, spp, attr) 4166 char *s; 4167 int c; 4168 synpat_T *spp; 4169 int attr; 4170 { 4171 long n; 4172 int mask; 4173 int first; 4174 static char *sepchars = "/+=-#@\"|'^&"; 4175 int i; 4176 4177 /* May have to write "matchgroup=group" */ 4178 if (last_matchgroup != spp->sp_syn_match_id) 4179 { 4180 last_matchgroup = spp->sp_syn_match_id; 4181 msg_puts_attr((char_u *)"matchgroup", attr); 4182 msg_putchar('='); 4183 if (last_matchgroup == 0) 4184 msg_outtrans((char_u *)"NONE"); 4185 else 4186 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name); 4187 msg_putchar(' '); 4188 } 4189 4190 /* output the name of the pattern and an '=' or ' ' */ 4191 msg_puts_attr((char_u *)s, attr); 4192 msg_putchar(c); 4193 4194 /* output the pattern, in between a char that is not in the pattern */ 4195 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; ) 4196 if (sepchars[++i] == NUL) 4197 { 4198 i = 0; /* no good char found, just use the first one */ 4199 break; 4200 } 4201 msg_putchar(sepchars[i]); 4202 msg_outtrans(spp->sp_pattern); 4203 msg_putchar(sepchars[i]); 4204 4205 /* output any pattern options */ 4206 first = TRUE; 4207 for (i = 0; i < SPO_COUNT; ++i) 4208 { 4209 mask = (1 << i); 4210 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT))) 4211 { 4212 if (!first) 4213 msg_putchar(','); /* separate with commas */ 4214 msg_puts((char_u *)spo_name_tab[i]); 4215 n = spp->sp_offsets[i]; 4216 if (i != SPO_LC_OFF) 4217 { 4218 if (spp->sp_off_flags & mask) 4219 msg_putchar('s'); 4220 else 4221 msg_putchar('e'); 4222 if (n > 0) 4223 msg_putchar('+'); 4224 } 4225 if (n || i == SPO_LC_OFF) 4226 msg_outnum(n); 4227 first = FALSE; 4228 } 4229 } 4230 msg_putchar(' '); 4231 } 4232 4233 /* 4234 * List or clear the keywords for one syntax group. 4235 * Return TRUE if the header has been printed. 4236 */ 4237 static int 4238 syn_list_keywords(id, ht, did_header, attr) 4239 int id; 4240 hashtab_T *ht; 4241 int did_header; /* header has already been printed */ 4242 int attr; 4243 { 4244 int outlen; 4245 hashitem_T *hi; 4246 keyentry_T *kp; 4247 int todo; 4248 int prev_contained = 0; 4249 short *prev_next_list = NULL; 4250 short *prev_cont_in_list = NULL; 4251 int prev_skipnl = 0; 4252 int prev_skipwhite = 0; 4253 int prev_skipempty = 0; 4254 4255 /* 4256 * Unfortunately, this list of keywords is not sorted on alphabet but on 4257 * hash value... 4258 */ 4259 todo = (int)ht->ht_used; 4260 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi) 4261 { 4262 if (!HASHITEM_EMPTY(hi)) 4263 { 4264 --todo; 4265 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next) 4266 { 4267 if (kp->k_syn.id == id) 4268 { 4269 if (prev_contained != (kp->flags & HL_CONTAINED) 4270 || prev_skipnl != (kp->flags & HL_SKIPNL) 4271 || prev_skipwhite != (kp->flags & HL_SKIPWHITE) 4272 || prev_skipempty != (kp->flags & HL_SKIPEMPTY) 4273 || prev_cont_in_list != kp->k_syn.cont_in_list 4274 || prev_next_list != kp->next_list) 4275 outlen = 9999; 4276 else 4277 outlen = (int)STRLEN(kp->keyword); 4278 /* output "contained" and "nextgroup" on each line */ 4279 if (syn_list_header(did_header, outlen, id)) 4280 { 4281 prev_contained = 0; 4282 prev_next_list = NULL; 4283 prev_cont_in_list = NULL; 4284 prev_skipnl = 0; 4285 prev_skipwhite = 0; 4286 prev_skipempty = 0; 4287 } 4288 did_header = TRUE; 4289 if (prev_contained != (kp->flags & HL_CONTAINED)) 4290 { 4291 msg_puts_attr((char_u *)"contained", attr); 4292 msg_putchar(' '); 4293 prev_contained = (kp->flags & HL_CONTAINED); 4294 } 4295 if (kp->k_syn.cont_in_list != prev_cont_in_list) 4296 { 4297 put_id_list((char_u *)"containedin", 4298 kp->k_syn.cont_in_list, attr); 4299 msg_putchar(' '); 4300 prev_cont_in_list = kp->k_syn.cont_in_list; 4301 } 4302 if (kp->next_list != prev_next_list) 4303 { 4304 put_id_list((char_u *)"nextgroup", kp->next_list, attr); 4305 msg_putchar(' '); 4306 prev_next_list = kp->next_list; 4307 if (kp->flags & HL_SKIPNL) 4308 { 4309 msg_puts_attr((char_u *)"skipnl", attr); 4310 msg_putchar(' '); 4311 prev_skipnl = (kp->flags & HL_SKIPNL); 4312 } 4313 if (kp->flags & HL_SKIPWHITE) 4314 { 4315 msg_puts_attr((char_u *)"skipwhite", attr); 4316 msg_putchar(' '); 4317 prev_skipwhite = (kp->flags & HL_SKIPWHITE); 4318 } 4319 if (kp->flags & HL_SKIPEMPTY) 4320 { 4321 msg_puts_attr((char_u *)"skipempty", attr); 4322 msg_putchar(' '); 4323 prev_skipempty = (kp->flags & HL_SKIPEMPTY); 4324 } 4325 } 4326 msg_outtrans(kp->keyword); 4327 } 4328 } 4329 } 4330 } 4331 4332 return did_header; 4333 } 4334 4335 static void 4336 syn_clear_keyword(id, ht) 4337 int id; 4338 hashtab_T *ht; 4339 { 4340 hashitem_T *hi; 4341 keyentry_T *kp; 4342 keyentry_T *kp_prev; 4343 keyentry_T *kp_next; 4344 int todo; 4345 4346 hash_lock(ht); 4347 todo = (int)ht->ht_used; 4348 for (hi = ht->ht_array; todo > 0; ++hi) 4349 { 4350 if (!HASHITEM_EMPTY(hi)) 4351 { 4352 --todo; 4353 kp_prev = NULL; 4354 for (kp = HI2KE(hi); kp != NULL; ) 4355 { 4356 if (kp->k_syn.id == id) 4357 { 4358 kp_next = kp->ke_next; 4359 if (kp_prev == NULL) 4360 { 4361 if (kp_next == NULL) 4362 hash_remove(ht, hi); 4363 else 4364 hi->hi_key = KE2HIKEY(kp_next); 4365 } 4366 else 4367 kp_prev->ke_next = kp_next; 4368 vim_free(kp->next_list); 4369 vim_free(kp->k_syn.cont_in_list); 4370 vim_free(kp); 4371 kp = kp_next; 4372 } 4373 else 4374 { 4375 kp_prev = kp; 4376 kp = kp->ke_next; 4377 } 4378 } 4379 } 4380 } 4381 hash_unlock(ht); 4382 } 4383 4384 /* 4385 * Clear a whole keyword table. 4386 */ 4387 static void 4388 clear_keywtab(ht) 4389 hashtab_T *ht; 4390 { 4391 hashitem_T *hi; 4392 int todo; 4393 keyentry_T *kp; 4394 keyentry_T *kp_next; 4395 4396 todo = (int)ht->ht_used; 4397 for (hi = ht->ht_array; todo > 0; ++hi) 4398 { 4399 if (!HASHITEM_EMPTY(hi)) 4400 { 4401 --todo; 4402 for (kp = HI2KE(hi); kp != NULL; kp = kp_next) 4403 { 4404 kp_next = kp->ke_next; 4405 vim_free(kp->next_list); 4406 vim_free(kp->k_syn.cont_in_list); 4407 vim_free(kp); 4408 } 4409 } 4410 } 4411 hash_clear(ht); 4412 hash_init(ht); 4413 } 4414 4415 /* 4416 * Add a keyword to the list of keywords. 4417 */ 4418 static void 4419 add_keyword(name, id, flags, cont_in_list, next_list, conceal_char) 4420 char_u *name; /* name of keyword */ 4421 int id; /* group ID for this keyword */ 4422 int flags; /* flags for this keyword */ 4423 short *cont_in_list; /* containedin for this keyword */ 4424 short *next_list; /* nextgroup for this keyword */ 4425 int conceal_char; 4426 { 4427 keyentry_T *kp; 4428 hashtab_T *ht; 4429 hashitem_T *hi; 4430 char_u *name_ic; 4431 long_u hash; 4432 char_u name_folded[MAXKEYWLEN + 1]; 4433 4434 if (curwin->w_s->b_syn_ic) 4435 name_ic = str_foldcase(name, (int)STRLEN(name), 4436 name_folded, MAXKEYWLEN + 1); 4437 else 4438 name_ic = name; 4439 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic))); 4440 if (kp == NULL) 4441 return; 4442 STRCPY(kp->keyword, name_ic); 4443 kp->k_syn.id = id; 4444 kp->k_syn.inc_tag = current_syn_inc_tag; 4445 kp->flags = flags; 4446 kp->k_char = conceal_char; 4447 kp->k_syn.cont_in_list = copy_id_list(cont_in_list); 4448 if (cont_in_list != NULL) 4449 curwin->w_s->b_syn_containedin = TRUE; 4450 kp->next_list = copy_id_list(next_list); 4451 4452 if (curwin->w_s->b_syn_ic) 4453 ht = &curwin->w_s->b_keywtab_ic; 4454 else 4455 ht = &curwin->w_s->b_keywtab; 4456 4457 hash = hash_hash(kp->keyword); 4458 hi = hash_lookup(ht, kp->keyword, hash); 4459 if (HASHITEM_EMPTY(hi)) 4460 { 4461 /* new keyword, add to hashtable */ 4462 kp->ke_next = NULL; 4463 hash_add_item(ht, hi, kp->keyword, hash); 4464 } 4465 else 4466 { 4467 /* keyword already exists, prepend to list */ 4468 kp->ke_next = HI2KE(hi); 4469 hi->hi_key = KE2HIKEY(kp); 4470 } 4471 } 4472 4473 /* 4474 * Get the start and end of the group name argument. 4475 * Return a pointer to the first argument. 4476 * Return NULL if the end of the command was found instead of further args. 4477 */ 4478 static char_u * 4479 get_group_name(arg, name_end) 4480 char_u *arg; /* start of the argument */ 4481 char_u **name_end; /* pointer to end of the name */ 4482 { 4483 char_u *rest; 4484 4485 *name_end = skiptowhite(arg); 4486 rest = skipwhite(*name_end); 4487 4488 /* 4489 * Check if there are enough arguments. The first argument may be a 4490 * pattern, where '|' is allowed, so only check for NUL. 4491 */ 4492 if (ends_excmd(*arg) || *rest == NUL) 4493 return NULL; 4494 return rest; 4495 } 4496 4497 /* 4498 * Check for syntax command option arguments. 4499 * This can be called at any place in the list of arguments, and just picks 4500 * out the arguments that are known. Can be called several times in a row to 4501 * collect all options in between other arguments. 4502 * Return a pointer to the next argument (which isn't an option). 4503 * Return NULL for any error; 4504 */ 4505 static char_u * 4506 get_syn_options(arg, opt, conceal_char) 4507 char_u *arg; /* next argument to be checked */ 4508 syn_opt_arg_T *opt; /* various things */ 4509 int *conceal_char UNUSED; 4510 { 4511 char_u *gname_start, *gname; 4512 int syn_id; 4513 int len; 4514 char *p; 4515 int i; 4516 int fidx; 4517 static struct flag 4518 { 4519 char *name; 4520 int argtype; 4521 int flags; 4522 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED}, 4523 {"oOnNeElLiInNeE", 0, HL_ONELINE}, 4524 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND}, 4525 {"eExXtTeEnNdD", 0, HL_EXTEND}, 4526 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL}, 4527 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP}, 4528 {"sSkKiIpPnNlL", 0, HL_SKIPNL}, 4529 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE}, 4530 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY}, 4531 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE}, 4532 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE}, 4533 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY}, 4534 {"fFoOlLdD", 0, HL_FOLD}, 4535 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL}, 4536 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS}, 4537 {"cCcChHaArR", 11, 0}, 4538 {"cCoOnNtTaAiInNsS", 1, 0}, 4539 {"cCoOnNtTaAiInNeEdDiInN", 2, 0}, 4540 {"nNeExXtTgGrRoOuUpP", 3, 0}, 4541 }; 4542 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN"; 4543 4544 if (arg == NULL) /* already detected error */ 4545 return NULL; 4546 4547 #ifdef FEAT_CONCEAL 4548 if (curwin->w_s->b_syn_conceal) 4549 opt->flags |= HL_CONCEAL; 4550 #endif 4551 4552 for (;;) 4553 { 4554 /* 4555 * This is used very often when a large number of keywords is defined. 4556 * Need to skip quickly when no option name is found. 4557 * Also avoid tolower(), it's slow. 4558 */ 4559 if (strchr(first_letters, *arg) == NULL) 4560 break; 4561 4562 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; ) 4563 { 4564 p = flagtab[fidx].name; 4565 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len) 4566 if (arg[len] != p[i] && arg[len] != p[i + 1]) 4567 break; 4568 if (p[i] == NUL && (vim_iswhite(arg[len]) 4569 || (flagtab[fidx].argtype > 0 4570 ? arg[len] == '=' 4571 : ends_excmd(arg[len])))) 4572 { 4573 if (opt->keyword 4574 && (flagtab[fidx].flags == HL_DISPLAY 4575 || flagtab[fidx].flags == HL_FOLD 4576 || flagtab[fidx].flags == HL_EXTEND)) 4577 /* treat "display", "fold" and "extend" as a keyword */ 4578 fidx = -1; 4579 break; 4580 } 4581 } 4582 if (fidx < 0) /* no match found */ 4583 break; 4584 4585 if (flagtab[fidx].argtype == 1) 4586 { 4587 if (!opt->has_cont_list) 4588 { 4589 EMSG(_("E395: contains argument not accepted here")); 4590 return NULL; 4591 } 4592 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL) 4593 return NULL; 4594 } 4595 else if (flagtab[fidx].argtype == 2) 4596 { 4597 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL) 4598 return NULL; 4599 } 4600 else if (flagtab[fidx].argtype == 3) 4601 { 4602 if (get_id_list(&arg, 9, &opt->next_list) == FAIL) 4603 return NULL; 4604 } 4605 else if (flagtab[fidx].argtype == 11 && arg[5] == '=') 4606 { 4607 #ifdef FEAT_MBYTE 4608 /* cchar=? */ 4609 if (has_mbyte) 4610 { 4611 # ifdef FEAT_CONCEAL 4612 *conceal_char = mb_ptr2char(arg + 6); 4613 # endif 4614 arg += mb_ptr2len(arg + 6) - 1; 4615 } 4616 else 4617 #endif 4618 { 4619 #ifdef FEAT_CONCEAL 4620 *conceal_char = arg[6]; 4621 #else 4622 ; 4623 #endif 4624 } 4625 #ifdef FEAT_CONCEAL 4626 if (!vim_isprintc_strict(*conceal_char)) 4627 { 4628 EMSG(_("E844: invalid cchar value")); 4629 return NULL; 4630 } 4631 #endif 4632 arg = skipwhite(arg + 7); 4633 } 4634 else 4635 { 4636 opt->flags |= flagtab[fidx].flags; 4637 arg = skipwhite(arg + len); 4638 4639 if (flagtab[fidx].flags == HL_SYNC_HERE 4640 || flagtab[fidx].flags == HL_SYNC_THERE) 4641 { 4642 if (opt->sync_idx == NULL) 4643 { 4644 EMSG(_("E393: group[t]here not accepted here")); 4645 return NULL; 4646 } 4647 gname_start = arg; 4648 arg = skiptowhite(arg); 4649 if (gname_start == arg) 4650 return NULL; 4651 gname = vim_strnsave(gname_start, (int)(arg - gname_start)); 4652 if (gname == NULL) 4653 return NULL; 4654 if (STRCMP(gname, "NONE") == 0) 4655 *opt->sync_idx = NONE_IDX; 4656 else 4657 { 4658 syn_id = syn_name2id(gname); 4659 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; ) 4660 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id 4661 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START) 4662 { 4663 *opt->sync_idx = i; 4664 break; 4665 } 4666 if (i < 0) 4667 { 4668 EMSG2(_("E394: Didn't find region item for %s"), gname); 4669 vim_free(gname); 4670 return NULL; 4671 } 4672 } 4673 4674 vim_free(gname); 4675 arg = skipwhite(arg); 4676 } 4677 #ifdef FEAT_FOLDING 4678 else if (flagtab[fidx].flags == HL_FOLD 4679 && foldmethodIsSyntax(curwin)) 4680 /* Need to update folds later. */ 4681 foldUpdateAll(curwin); 4682 #endif 4683 } 4684 } 4685 4686 return arg; 4687 } 4688 4689 /* 4690 * Adjustments to syntax item when declared in a ":syn include"'d file. 4691 * Set the contained flag, and if the item is not already contained, add it 4692 * to the specified top-level group, if any. 4693 */ 4694 static void 4695 syn_incl_toplevel(id, flagsp) 4696 int id; 4697 int *flagsp; 4698 { 4699 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0) 4700 return; 4701 *flagsp |= HL_CONTAINED; 4702 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER) 4703 { 4704 /* We have to alloc this, because syn_combine_list() will free it. */ 4705 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short))); 4706 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER; 4707 4708 if (grp_list != NULL) 4709 { 4710 grp_list[0] = id; 4711 grp_list[1] = 0; 4712 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list, 4713 CLUSTER_ADD); 4714 } 4715 } 4716 } 4717 4718 /* 4719 * Handle ":syntax include [@{group-name}] filename" command. 4720 */ 4721 static void 4722 syn_cmd_include(eap, syncing) 4723 exarg_T *eap; 4724 int syncing UNUSED; 4725 { 4726 char_u *arg = eap->arg; 4727 int sgl_id = 1; 4728 char_u *group_name_end; 4729 char_u *rest; 4730 char_u *errormsg = NULL; 4731 int prev_toplvl_grp; 4732 int prev_syn_inc_tag; 4733 int source = FALSE; 4734 4735 eap->nextcmd = find_nextcmd(arg); 4736 if (eap->skip) 4737 return; 4738 4739 if (arg[0] == '@') 4740 { 4741 ++arg; 4742 rest = get_group_name(arg, &group_name_end); 4743 if (rest == NULL) 4744 { 4745 EMSG((char_u *)_("E397: Filename required")); 4746 return; 4747 } 4748 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg)); 4749 if (sgl_id == 0) 4750 return; 4751 /* separate_nextcmd() and expand_filename() depend on this */ 4752 eap->arg = rest; 4753 } 4754 4755 /* 4756 * Everything that's left, up to the next command, should be the 4757 * filename to include. 4758 */ 4759 eap->argt |= (XFILE | NOSPC); 4760 separate_nextcmd(eap); 4761 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg)) 4762 { 4763 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the 4764 * file. Need to expand the file name first. In other cases 4765 * ":runtime!" is used. */ 4766 source = TRUE; 4767 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL) 4768 { 4769 if (errormsg != NULL) 4770 EMSG(errormsg); 4771 return; 4772 } 4773 } 4774 4775 /* 4776 * Save and restore the existing top-level grouplist id and ":syn 4777 * include" tag around the actual inclusion. 4778 */ 4779 if (running_syn_inc_tag >= MAX_SYN_INC_TAG) 4780 { 4781 EMSG((char_u *)_("E847: Too many syntax includes")); 4782 return; 4783 } 4784 prev_syn_inc_tag = current_syn_inc_tag; 4785 current_syn_inc_tag = ++running_syn_inc_tag; 4786 prev_toplvl_grp = curwin->w_s->b_syn_topgrp; 4787 curwin->w_s->b_syn_topgrp = sgl_id; 4788 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL 4789 : source_runtime(eap->arg, TRUE) == FAIL) 4790 EMSG2(_(e_notopen), eap->arg); 4791 curwin->w_s->b_syn_topgrp = prev_toplvl_grp; 4792 current_syn_inc_tag = prev_syn_inc_tag; 4793 } 4794 4795 /* 4796 * Handle ":syntax keyword {group-name} [{option}] keyword .." command. 4797 */ 4798 static void 4799 syn_cmd_keyword(eap, syncing) 4800 exarg_T *eap; 4801 int syncing UNUSED; 4802 { 4803 char_u *arg = eap->arg; 4804 char_u *group_name_end; 4805 int syn_id; 4806 char_u *rest; 4807 char_u *keyword_copy = NULL; 4808 char_u *p; 4809 char_u *kw; 4810 syn_opt_arg_T syn_opt_arg; 4811 int cnt; 4812 int conceal_char = NUL; 4813 4814 rest = get_group_name(arg, &group_name_end); 4815 4816 if (rest != NULL) 4817 { 4818 syn_id = syn_check_group(arg, (int)(group_name_end - arg)); 4819 if (syn_id != 0) 4820 /* allocate a buffer, for removing backslashes in the keyword */ 4821 keyword_copy = alloc((unsigned)STRLEN(rest) + 1); 4822 if (keyword_copy != NULL) 4823 { 4824 syn_opt_arg.flags = 0; 4825 syn_opt_arg.keyword = TRUE; 4826 syn_opt_arg.sync_idx = NULL; 4827 syn_opt_arg.has_cont_list = FALSE; 4828 syn_opt_arg.cont_in_list = NULL; 4829 syn_opt_arg.next_list = NULL; 4830 4831 /* 4832 * The options given apply to ALL keywords, so all options must be 4833 * found before keywords can be created. 4834 * 1: collect the options and copy the keywords to keyword_copy. 4835 */ 4836 cnt = 0; 4837 p = keyword_copy; 4838 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest)) 4839 { 4840 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char); 4841 if (rest == NULL || ends_excmd(*rest)) 4842 break; 4843 /* Copy the keyword, removing backslashes, and add a NUL. */ 4844 while (*rest != NUL && !vim_iswhite(*rest)) 4845 { 4846 if (*rest == '\\' && rest[1] != NUL) 4847 ++rest; 4848 *p++ = *rest++; 4849 } 4850 *p++ = NUL; 4851 ++cnt; 4852 } 4853 4854 if (!eap->skip) 4855 { 4856 /* Adjust flags for use of ":syn include". */ 4857 syn_incl_toplevel(syn_id, &syn_opt_arg.flags); 4858 4859 /* 4860 * 2: Add an entry for each keyword. 4861 */ 4862 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1) 4863 { 4864 for (p = vim_strchr(kw, '['); ; ) 4865 { 4866 if (p != NULL) 4867 *p = NUL; 4868 add_keyword(kw, syn_id, syn_opt_arg.flags, 4869 syn_opt_arg.cont_in_list, 4870 syn_opt_arg.next_list, conceal_char); 4871 if (p == NULL) 4872 break; 4873 if (p[1] == NUL) 4874 { 4875 EMSG2(_("E789: Missing ']': %s"), kw); 4876 goto error; 4877 } 4878 if (p[1] == ']') 4879 { 4880 if (p[2] != NUL) 4881 { 4882 EMSG3(_("E890: trailing char after ']': %s]%s"), 4883 kw, &p[2]); 4884 goto error; 4885 } 4886 kw = p + 1; /* skip over the "]" */ 4887 break; 4888 } 4889 #ifdef FEAT_MBYTE 4890 if (has_mbyte) 4891 { 4892 int l = (*mb_ptr2len)(p + 1); 4893 4894 mch_memmove(p, p + 1, l); 4895 p += l; 4896 } 4897 else 4898 #endif 4899 { 4900 p[0] = p[1]; 4901 ++p; 4902 } 4903 } 4904 } 4905 } 4906 error: 4907 vim_free(keyword_copy); 4908 vim_free(syn_opt_arg.cont_in_list); 4909 vim_free(syn_opt_arg.next_list); 4910 } 4911 } 4912 4913 if (rest != NULL) 4914 eap->nextcmd = check_nextcmd(rest); 4915 else 4916 EMSG2(_(e_invarg2), arg); 4917 4918 redraw_curbuf_later(SOME_VALID); 4919 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ 4920 } 4921 4922 /* 4923 * Handle ":syntax match {name} [{options}] {pattern} [{options}]". 4924 * 4925 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .." 4926 */ 4927 static void 4928 syn_cmd_match(eap, syncing) 4929 exarg_T *eap; 4930 int syncing; /* TRUE for ":syntax sync match .. " */ 4931 { 4932 char_u *arg = eap->arg; 4933 char_u *group_name_end; 4934 char_u *rest; 4935 synpat_T item; /* the item found in the line */ 4936 int syn_id; 4937 int idx; 4938 syn_opt_arg_T syn_opt_arg; 4939 int sync_idx = 0; 4940 int conceal_char = NUL; 4941 4942 /* Isolate the group name, check for validity */ 4943 rest = get_group_name(arg, &group_name_end); 4944 4945 /* Get options before the pattern */ 4946 syn_opt_arg.flags = 0; 4947 syn_opt_arg.keyword = FALSE; 4948 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL; 4949 syn_opt_arg.has_cont_list = TRUE; 4950 syn_opt_arg.cont_list = NULL; 4951 syn_opt_arg.cont_in_list = NULL; 4952 syn_opt_arg.next_list = NULL; 4953 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char); 4954 4955 /* get the pattern. */ 4956 init_syn_patterns(); 4957 vim_memset(&item, 0, sizeof(item)); 4958 rest = get_syn_pattern(rest, &item); 4959 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL)) 4960 syn_opt_arg.flags |= HL_HAS_EOL; 4961 4962 /* Get options after the pattern */ 4963 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char); 4964 4965 if (rest != NULL) /* all arguments are valid */ 4966 { 4967 /* 4968 * Check for trailing command and illegal trailing arguments. 4969 */ 4970 eap->nextcmd = check_nextcmd(rest); 4971 if (!ends_excmd(*rest) || eap->skip) 4972 rest = NULL; 4973 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL 4974 && (syn_id = syn_check_group(arg, 4975 (int)(group_name_end - arg))) != 0) 4976 { 4977 syn_incl_toplevel(syn_id, &syn_opt_arg.flags); 4978 /* 4979 * Store the pattern in the syn_items list 4980 */ 4981 idx = curwin->w_s->b_syn_patterns.ga_len; 4982 SYN_ITEMS(curwin->w_s)[idx] = item; 4983 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing; 4984 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH; 4985 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id; 4986 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag; 4987 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags; 4988 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx; 4989 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list; 4990 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list = 4991 syn_opt_arg.cont_in_list; 4992 #ifdef FEAT_CONCEAL 4993 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char; 4994 #endif 4995 if (syn_opt_arg.cont_in_list != NULL) 4996 curwin->w_s->b_syn_containedin = TRUE; 4997 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list; 4998 ++curwin->w_s->b_syn_patterns.ga_len; 4999 5000 /* remember that we found a match for syncing on */ 5001 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE)) 5002 curwin->w_s->b_syn_sync_flags |= SF_MATCH; 5003 #ifdef FEAT_FOLDING 5004 if (syn_opt_arg.flags & HL_FOLD) 5005 ++curwin->w_s->b_syn_folditems; 5006 #endif 5007 5008 redraw_curbuf_later(SOME_VALID); 5009 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ 5010 return; /* don't free the progs and patterns now */ 5011 } 5012 } 5013 5014 /* 5015 * Something failed, free the allocated memory. 5016 */ 5017 vim_regfree(item.sp_prog); 5018 vim_free(item.sp_pattern); 5019 vim_free(syn_opt_arg.cont_list); 5020 vim_free(syn_opt_arg.cont_in_list); 5021 vim_free(syn_opt_arg.next_list); 5022 5023 if (rest == NULL) 5024 EMSG2(_(e_invarg2), arg); 5025 } 5026 5027 /* 5028 * Handle ":syntax region {group-name} [matchgroup={group-name}] 5029 * start {start} .. [skip {skip}] end {end} .. [{options}]". 5030 */ 5031 static void 5032 syn_cmd_region(eap, syncing) 5033 exarg_T *eap; 5034 int syncing; /* TRUE for ":syntax sync region .." */ 5035 { 5036 char_u *arg = eap->arg; 5037 char_u *group_name_end; 5038 char_u *rest; /* next arg, NULL on error */ 5039 char_u *key_end; 5040 char_u *key = NULL; 5041 char_u *p; 5042 int item; 5043 #define ITEM_START 0 5044 #define ITEM_SKIP 1 5045 #define ITEM_END 2 5046 #define ITEM_MATCHGROUP 3 5047 struct pat_ptr 5048 { 5049 synpat_T *pp_synp; /* pointer to syn_pattern */ 5050 int pp_matchgroup_id; /* matchgroup ID */ 5051 struct pat_ptr *pp_next; /* pointer to next pat_ptr */ 5052 } *(pat_ptrs[3]); 5053 /* patterns found in the line */ 5054 struct pat_ptr *ppp; 5055 struct pat_ptr *ppp_next; 5056 int pat_count = 0; /* nr of syn_patterns found */ 5057 int syn_id; 5058 int matchgroup_id = 0; 5059 int not_enough = FALSE; /* not enough arguments */ 5060 int illegal = FALSE; /* illegal arguments */ 5061 int success = FALSE; 5062 int idx; 5063 syn_opt_arg_T syn_opt_arg; 5064 int conceal_char = NUL; 5065 5066 /* Isolate the group name, check for validity */ 5067 rest = get_group_name(arg, &group_name_end); 5068 5069 pat_ptrs[0] = NULL; 5070 pat_ptrs[1] = NULL; 5071 pat_ptrs[2] = NULL; 5072 5073 init_syn_patterns(); 5074 5075 syn_opt_arg.flags = 0; 5076 syn_opt_arg.keyword = FALSE; 5077 syn_opt_arg.sync_idx = NULL; 5078 syn_opt_arg.has_cont_list = TRUE; 5079 syn_opt_arg.cont_list = NULL; 5080 syn_opt_arg.cont_in_list = NULL; 5081 syn_opt_arg.next_list = NULL; 5082 5083 /* 5084 * get the options, patterns and matchgroup. 5085 */ 5086 while (rest != NULL && !ends_excmd(*rest)) 5087 { 5088 /* Check for option arguments */ 5089 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char); 5090 if (rest == NULL || ends_excmd(*rest)) 5091 break; 5092 5093 /* must be a pattern or matchgroup then */ 5094 key_end = rest; 5095 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=') 5096 ++key_end; 5097 vim_free(key); 5098 key = vim_strnsave_up(rest, (int)(key_end - rest)); 5099 if (key == NULL) /* out of memory */ 5100 { 5101 rest = NULL; 5102 break; 5103 } 5104 if (STRCMP(key, "MATCHGROUP") == 0) 5105 item = ITEM_MATCHGROUP; 5106 else if (STRCMP(key, "START") == 0) 5107 item = ITEM_START; 5108 else if (STRCMP(key, "END") == 0) 5109 item = ITEM_END; 5110 else if (STRCMP(key, "SKIP") == 0) 5111 { 5112 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */ 5113 { 5114 illegal = TRUE; 5115 break; 5116 } 5117 item = ITEM_SKIP; 5118 } 5119 else 5120 break; 5121 rest = skipwhite(key_end); 5122 if (*rest != '=') 5123 { 5124 rest = NULL; 5125 EMSG2(_("E398: Missing '=': %s"), arg); 5126 break; 5127 } 5128 rest = skipwhite(rest + 1); 5129 if (*rest == NUL) 5130 { 5131 not_enough = TRUE; 5132 break; 5133 } 5134 5135 if (item == ITEM_MATCHGROUP) 5136 { 5137 p = skiptowhite(rest); 5138 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip) 5139 matchgroup_id = 0; 5140 else 5141 { 5142 matchgroup_id = syn_check_group(rest, (int)(p - rest)); 5143 if (matchgroup_id == 0) 5144 { 5145 illegal = TRUE; 5146 break; 5147 } 5148 } 5149 rest = skipwhite(p); 5150 } 5151 else 5152 { 5153 /* 5154 * Allocate room for a syn_pattern, and link it in the list of 5155 * syn_patterns for this item, at the start (because the list is 5156 * used from end to start). 5157 */ 5158 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr)); 5159 if (ppp == NULL) 5160 { 5161 rest = NULL; 5162 break; 5163 } 5164 ppp->pp_next = pat_ptrs[item]; 5165 pat_ptrs[item] = ppp; 5166 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T)); 5167 if (ppp->pp_synp == NULL) 5168 { 5169 rest = NULL; 5170 break; 5171 } 5172 5173 /* 5174 * Get the syntax pattern and the following offset(s). 5175 */ 5176 /* Enable the appropriate \z specials. */ 5177 if (item == ITEM_START) 5178 reg_do_extmatch = REX_SET; 5179 else if (item == ITEM_SKIP || item == ITEM_END) 5180 reg_do_extmatch = REX_USE; 5181 rest = get_syn_pattern(rest, ppp->pp_synp); 5182 reg_do_extmatch = 0; 5183 if (item == ITEM_END && vim_regcomp_had_eol() 5184 && !(syn_opt_arg.flags & HL_EXCLUDENL)) 5185 ppp->pp_synp->sp_flags |= HL_HAS_EOL; 5186 ppp->pp_matchgroup_id = matchgroup_id; 5187 ++pat_count; 5188 } 5189 } 5190 vim_free(key); 5191 if (illegal || not_enough) 5192 rest = NULL; 5193 5194 /* 5195 * Must have a "start" and "end" pattern. 5196 */ 5197 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL || 5198 pat_ptrs[ITEM_END] == NULL)) 5199 { 5200 not_enough = TRUE; 5201 rest = NULL; 5202 } 5203 5204 if (rest != NULL) 5205 { 5206 /* 5207 * Check for trailing garbage or command. 5208 * If OK, add the item. 5209 */ 5210 eap->nextcmd = check_nextcmd(rest); 5211 if (!ends_excmd(*rest) || eap->skip) 5212 rest = NULL; 5213 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL 5214 && (syn_id = syn_check_group(arg, 5215 (int)(group_name_end - arg))) != 0) 5216 { 5217 syn_incl_toplevel(syn_id, &syn_opt_arg.flags); 5218 /* 5219 * Store the start/skip/end in the syn_items list 5220 */ 5221 idx = curwin->w_s->b_syn_patterns.ga_len; 5222 for (item = ITEM_START; item <= ITEM_END; ++item) 5223 { 5224 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next) 5225 { 5226 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp); 5227 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing; 5228 SYN_ITEMS(curwin->w_s)[idx].sp_type = 5229 (item == ITEM_START) ? SPTYPE_START : 5230 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END; 5231 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags; 5232 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id; 5233 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = 5234 current_syn_inc_tag; 5235 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id = 5236 ppp->pp_matchgroup_id; 5237 #ifdef FEAT_CONCEAL 5238 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char; 5239 #endif 5240 if (item == ITEM_START) 5241 { 5242 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = 5243 syn_opt_arg.cont_list; 5244 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list = 5245 syn_opt_arg.cont_in_list; 5246 if (syn_opt_arg.cont_in_list != NULL) 5247 curwin->w_s->b_syn_containedin = TRUE; 5248 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = 5249 syn_opt_arg.next_list; 5250 } 5251 ++curwin->w_s->b_syn_patterns.ga_len; 5252 ++idx; 5253 #ifdef FEAT_FOLDING 5254 if (syn_opt_arg.flags & HL_FOLD) 5255 ++curwin->w_s->b_syn_folditems; 5256 #endif 5257 } 5258 } 5259 5260 redraw_curbuf_later(SOME_VALID); 5261 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ 5262 success = TRUE; /* don't free the progs and patterns now */ 5263 } 5264 } 5265 5266 /* 5267 * Free the allocated memory. 5268 */ 5269 for (item = ITEM_START; item <= ITEM_END; ++item) 5270 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next) 5271 { 5272 if (!success) 5273 { 5274 vim_regfree(ppp->pp_synp->sp_prog); 5275 vim_free(ppp->pp_synp->sp_pattern); 5276 } 5277 vim_free(ppp->pp_synp); 5278 ppp_next = ppp->pp_next; 5279 vim_free(ppp); 5280 } 5281 5282 if (!success) 5283 { 5284 vim_free(syn_opt_arg.cont_list); 5285 vim_free(syn_opt_arg.cont_in_list); 5286 vim_free(syn_opt_arg.next_list); 5287 if (not_enough) 5288 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg); 5289 else if (illegal || rest == NULL) 5290 EMSG2(_(e_invarg2), arg); 5291 } 5292 } 5293 5294 /* 5295 * A simple syntax group ID comparison function suitable for use in qsort() 5296 */ 5297 static int 5298 #ifdef __BORLANDC__ 5299 _RTLENTRYF 5300 #endif 5301 syn_compare_stub(v1, v2) 5302 const void *v1; 5303 const void *v2; 5304 { 5305 const short *s1 = v1; 5306 const short *s2 = v2; 5307 5308 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0); 5309 } 5310 5311 /* 5312 * Combines lists of syntax clusters. 5313 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed. 5314 */ 5315 static void 5316 syn_combine_list(clstr1, clstr2, list_op) 5317 short **clstr1; 5318 short **clstr2; 5319 int list_op; 5320 { 5321 int count1 = 0; 5322 int count2 = 0; 5323 short *g1; 5324 short *g2; 5325 short *clstr = NULL; 5326 int count; 5327 int round; 5328 5329 /* 5330 * Handle degenerate cases. 5331 */ 5332 if (*clstr2 == NULL) 5333 return; 5334 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE) 5335 { 5336 if (list_op == CLUSTER_REPLACE) 5337 vim_free(*clstr1); 5338 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD) 5339 *clstr1 = *clstr2; 5340 else 5341 vim_free(*clstr2); 5342 return; 5343 } 5344 5345 for (g1 = *clstr1; *g1; g1++) 5346 ++count1; 5347 for (g2 = *clstr2; *g2; g2++) 5348 ++count2; 5349 5350 /* 5351 * For speed purposes, sort both lists. 5352 */ 5353 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub); 5354 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub); 5355 5356 /* 5357 * We proceed in two passes; in round 1, we count the elements to place 5358 * in the new list, and in round 2, we allocate and populate the new 5359 * list. For speed, we use a mergesort-like method, adding the smaller 5360 * of the current elements in each list to the new list. 5361 */ 5362 for (round = 1; round <= 2; round++) 5363 { 5364 g1 = *clstr1; 5365 g2 = *clstr2; 5366 count = 0; 5367 5368 /* 5369 * First, loop through the lists until one of them is empty. 5370 */ 5371 while (*g1 && *g2) 5372 { 5373 /* 5374 * We always want to add from the first list. 5375 */ 5376 if (*g1 < *g2) 5377 { 5378 if (round == 2) 5379 clstr[count] = *g1; 5380 count++; 5381 g1++; 5382 continue; 5383 } 5384 /* 5385 * We only want to add from the second list if we're adding the 5386 * lists. 5387 */ 5388 if (list_op == CLUSTER_ADD) 5389 { 5390 if (round == 2) 5391 clstr[count] = *g2; 5392 count++; 5393 } 5394 if (*g1 == *g2) 5395 g1++; 5396 g2++; 5397 } 5398 5399 /* 5400 * Now add the leftovers from whichever list didn't get finished 5401 * first. As before, we only want to add from the second list if 5402 * we're adding the lists. 5403 */ 5404 for (; *g1; g1++, count++) 5405 if (round == 2) 5406 clstr[count] = *g1; 5407 if (list_op == CLUSTER_ADD) 5408 for (; *g2; g2++, count++) 5409 if (round == 2) 5410 clstr[count] = *g2; 5411 5412 if (round == 1) 5413 { 5414 /* 5415 * If the group ended up empty, we don't need to allocate any 5416 * space for it. 5417 */ 5418 if (count == 0) 5419 { 5420 clstr = NULL; 5421 break; 5422 } 5423 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short))); 5424 if (clstr == NULL) 5425 break; 5426 clstr[count] = 0; 5427 } 5428 } 5429 5430 /* 5431 * Finally, put the new list in place. 5432 */ 5433 vim_free(*clstr1); 5434 vim_free(*clstr2); 5435 *clstr1 = clstr; 5436 } 5437 5438 /* 5439 * Lookup a syntax cluster name and return it's ID. 5440 * If it is not found, 0 is returned. 5441 */ 5442 static int 5443 syn_scl_name2id(name) 5444 char_u *name; 5445 { 5446 int i; 5447 char_u *name_u; 5448 5449 /* Avoid using stricmp() too much, it's slow on some systems */ 5450 name_u = vim_strsave_up(name); 5451 if (name_u == NULL) 5452 return 0; 5453 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; ) 5454 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL 5455 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0) 5456 break; 5457 vim_free(name_u); 5458 return (i < 0 ? 0 : i + SYNID_CLUSTER); 5459 } 5460 5461 /* 5462 * Like syn_scl_name2id(), but take a pointer + length argument. 5463 */ 5464 static int 5465 syn_scl_namen2id(linep, len) 5466 char_u *linep; 5467 int len; 5468 { 5469 char_u *name; 5470 int id = 0; 5471 5472 name = vim_strnsave(linep, len); 5473 if (name != NULL) 5474 { 5475 id = syn_scl_name2id(name); 5476 vim_free(name); 5477 } 5478 return id; 5479 } 5480 5481 /* 5482 * Find syntax cluster name in the table and return it's ID. 5483 * The argument is a pointer to the name and the length of the name. 5484 * If it doesn't exist yet, a new entry is created. 5485 * Return 0 for failure. 5486 */ 5487 static int 5488 syn_check_cluster(pp, len) 5489 char_u *pp; 5490 int len; 5491 { 5492 int id; 5493 char_u *name; 5494 5495 name = vim_strnsave(pp, len); 5496 if (name == NULL) 5497 return 0; 5498 5499 id = syn_scl_name2id(name); 5500 if (id == 0) /* doesn't exist yet */ 5501 id = syn_add_cluster(name); 5502 else 5503 vim_free(name); 5504 return id; 5505 } 5506 5507 /* 5508 * Add new syntax cluster and return it's ID. 5509 * "name" must be an allocated string, it will be consumed. 5510 * Return 0 for failure. 5511 */ 5512 static int 5513 syn_add_cluster(name) 5514 char_u *name; 5515 { 5516 int len; 5517 5518 /* 5519 * First call for this growarray: init growing array. 5520 */ 5521 if (curwin->w_s->b_syn_clusters.ga_data == NULL) 5522 { 5523 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T); 5524 curwin->w_s->b_syn_clusters.ga_growsize = 10; 5525 } 5526 5527 len = curwin->w_s->b_syn_clusters.ga_len; 5528 if (len >= MAX_CLUSTER_ID) 5529 { 5530 EMSG((char_u *)_("E848: Too many syntax clusters")); 5531 vim_free(name); 5532 return 0; 5533 } 5534 5535 /* 5536 * Make room for at least one other cluster entry. 5537 */ 5538 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL) 5539 { 5540 vim_free(name); 5541 return 0; 5542 } 5543 5544 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T)); 5545 SYN_CLSTR(curwin->w_s)[len].scl_name = name; 5546 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name); 5547 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL; 5548 ++curwin->w_s->b_syn_clusters.ga_len; 5549 5550 if (STRICMP(name, "Spell") == 0) 5551 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER; 5552 if (STRICMP(name, "NoSpell") == 0) 5553 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER; 5554 5555 return len + SYNID_CLUSTER; 5556 } 5557 5558 /* 5559 * Handle ":syntax cluster {cluster-name} [contains={groupname},..] 5560 * [add={groupname},..] [remove={groupname},..]". 5561 */ 5562 static void 5563 syn_cmd_cluster(eap, syncing) 5564 exarg_T *eap; 5565 int syncing UNUSED; 5566 { 5567 char_u *arg = eap->arg; 5568 char_u *group_name_end; 5569 char_u *rest; 5570 int scl_id; 5571 short *clstr_list; 5572 int got_clstr = FALSE; 5573 int opt_len; 5574 int list_op; 5575 5576 eap->nextcmd = find_nextcmd(arg); 5577 if (eap->skip) 5578 return; 5579 5580 rest = get_group_name(arg, &group_name_end); 5581 5582 if (rest != NULL) 5583 { 5584 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg)); 5585 if (scl_id == 0) 5586 return; 5587 scl_id -= SYNID_CLUSTER; 5588 5589 for (;;) 5590 { 5591 if (STRNICMP(rest, "add", 3) == 0 5592 && (vim_iswhite(rest[3]) || rest[3] == '=')) 5593 { 5594 opt_len = 3; 5595 list_op = CLUSTER_ADD; 5596 } 5597 else if (STRNICMP(rest, "remove", 6) == 0 5598 && (vim_iswhite(rest[6]) || rest[6] == '=')) 5599 { 5600 opt_len = 6; 5601 list_op = CLUSTER_SUBTRACT; 5602 } 5603 else if (STRNICMP(rest, "contains", 8) == 0 5604 && (vim_iswhite(rest[8]) || rest[8] == '=')) 5605 { 5606 opt_len = 8; 5607 list_op = CLUSTER_REPLACE; 5608 } 5609 else 5610 break; 5611 5612 clstr_list = NULL; 5613 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL) 5614 { 5615 EMSG2(_(e_invarg2), rest); 5616 break; 5617 } 5618 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list, 5619 &clstr_list, list_op); 5620 got_clstr = TRUE; 5621 } 5622 5623 if (got_clstr) 5624 { 5625 redraw_curbuf_later(SOME_VALID); 5626 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */ 5627 } 5628 } 5629 5630 if (!got_clstr) 5631 EMSG(_("E400: No cluster specified")); 5632 if (rest == NULL || !ends_excmd(*rest)) 5633 EMSG2(_(e_invarg2), arg); 5634 } 5635 5636 /* 5637 * On first call for current buffer: Init growing array. 5638 */ 5639 static void 5640 init_syn_patterns() 5641 { 5642 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T); 5643 curwin->w_s->b_syn_patterns.ga_growsize = 10; 5644 } 5645 5646 /* 5647 * Get one pattern for a ":syntax match" or ":syntax region" command. 5648 * Stores the pattern and program in a synpat_T. 5649 * Returns a pointer to the next argument, or NULL in case of an error. 5650 */ 5651 static char_u * 5652 get_syn_pattern(arg, ci) 5653 char_u *arg; 5654 synpat_T *ci; 5655 { 5656 char_u *end; 5657 int *p; 5658 int idx; 5659 char_u *cpo_save; 5660 5661 /* need at least three chars */ 5662 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL) 5663 return NULL; 5664 5665 end = skip_regexp(arg + 1, *arg, TRUE, NULL); 5666 if (*end != *arg) /* end delimiter not found */ 5667 { 5668 EMSG2(_("E401: Pattern delimiter not found: %s"), arg); 5669 return NULL; 5670 } 5671 /* store the pattern and compiled regexp program */ 5672 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL) 5673 return NULL; 5674 5675 /* Make 'cpoptions' empty, to avoid the 'l' flag */ 5676 cpo_save = p_cpo; 5677 p_cpo = (char_u *)""; 5678 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC); 5679 p_cpo = cpo_save; 5680 5681 if (ci->sp_prog == NULL) 5682 return NULL; 5683 ci->sp_ic = curwin->w_s->b_syn_ic; 5684 #ifdef FEAT_PROFILE 5685 syn_clear_time(&ci->sp_time); 5686 #endif 5687 5688 /* 5689 * Check for a match, highlight or region offset. 5690 */ 5691 ++end; 5692 do 5693 { 5694 for (idx = SPO_COUNT; --idx >= 0; ) 5695 if (STRNCMP(end, spo_name_tab[idx], 3) == 0) 5696 break; 5697 if (idx >= 0) 5698 { 5699 p = &(ci->sp_offsets[idx]); 5700 if (idx != SPO_LC_OFF) 5701 switch (end[3]) 5702 { 5703 case 's': break; 5704 case 'b': break; 5705 case 'e': idx += SPO_COUNT; break; 5706 default: idx = -1; break; 5707 } 5708 if (idx >= 0) 5709 { 5710 ci->sp_off_flags |= (1 << idx); 5711 if (idx == SPO_LC_OFF) /* lc=99 */ 5712 { 5713 end += 3; 5714 *p = getdigits(&end); 5715 5716 /* "lc=" offset automatically sets "ms=" offset */ 5717 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF))) 5718 { 5719 ci->sp_off_flags |= (1 << SPO_MS_OFF); 5720 ci->sp_offsets[SPO_MS_OFF] = *p; 5721 } 5722 } 5723 else /* yy=x+99 */ 5724 { 5725 end += 4; 5726 if (*end == '+') 5727 { 5728 ++end; 5729 *p = getdigits(&end); /* positive offset */ 5730 } 5731 else if (*end == '-') 5732 { 5733 ++end; 5734 *p = -getdigits(&end); /* negative offset */ 5735 } 5736 } 5737 if (*end != ',') 5738 break; 5739 ++end; 5740 } 5741 } 5742 } while (idx >= 0); 5743 5744 if (!ends_excmd(*end) && !vim_iswhite(*end)) 5745 { 5746 EMSG2(_("E402: Garbage after pattern: %s"), arg); 5747 return NULL; 5748 } 5749 return skipwhite(end); 5750 } 5751 5752 /* 5753 * Handle ":syntax sync .." command. 5754 */ 5755 static void 5756 syn_cmd_sync(eap, syncing) 5757 exarg_T *eap; 5758 int syncing UNUSED; 5759 { 5760 char_u *arg_start = eap->arg; 5761 char_u *arg_end; 5762 char_u *key = NULL; 5763 char_u *next_arg; 5764 int illegal = FALSE; 5765 int finished = FALSE; 5766 long n; 5767 char_u *cpo_save; 5768 5769 if (ends_excmd(*arg_start)) 5770 { 5771 syn_cmd_list(eap, TRUE); 5772 return; 5773 } 5774 5775 while (!ends_excmd(*arg_start)) 5776 { 5777 arg_end = skiptowhite(arg_start); 5778 next_arg = skipwhite(arg_end); 5779 vim_free(key); 5780 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start)); 5781 if (STRCMP(key, "CCOMMENT") == 0) 5782 { 5783 if (!eap->skip) 5784 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT; 5785 if (!ends_excmd(*next_arg)) 5786 { 5787 arg_end = skiptowhite(next_arg); 5788 if (!eap->skip) 5789 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg, 5790 (int)(arg_end - next_arg)); 5791 next_arg = skipwhite(arg_end); 5792 } 5793 else if (!eap->skip) 5794 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment"); 5795 } 5796 else if ( STRNCMP(key, "LINES", 5) == 0 5797 || STRNCMP(key, "MINLINES", 8) == 0 5798 || STRNCMP(key, "MAXLINES", 8) == 0 5799 || STRNCMP(key, "LINEBREAKS", 10) == 0) 5800 { 5801 if (key[4] == 'S') 5802 arg_end = key + 6; 5803 else if (key[0] == 'L') 5804 arg_end = key + 11; 5805 else 5806 arg_end = key + 9; 5807 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end)) 5808 { 5809 illegal = TRUE; 5810 break; 5811 } 5812 n = getdigits(&arg_end); 5813 if (!eap->skip) 5814 { 5815 if (key[4] == 'B') 5816 curwin->w_s->b_syn_sync_linebreaks = n; 5817 else if (key[1] == 'A') 5818 curwin->w_s->b_syn_sync_maxlines = n; 5819 else 5820 curwin->w_s->b_syn_sync_minlines = n; 5821 } 5822 } 5823 else if (STRCMP(key, "FROMSTART") == 0) 5824 { 5825 if (!eap->skip) 5826 { 5827 curwin->w_s->b_syn_sync_minlines = MAXLNUM; 5828 curwin->w_s->b_syn_sync_maxlines = 0; 5829 } 5830 } 5831 else if (STRCMP(key, "LINECONT") == 0) 5832 { 5833 if (curwin->w_s->b_syn_linecont_pat != NULL) 5834 { 5835 EMSG(_("E403: syntax sync: line continuations pattern specified twice")); 5836 finished = TRUE; 5837 break; 5838 } 5839 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL); 5840 if (*arg_end != *next_arg) /* end delimiter not found */ 5841 { 5842 illegal = TRUE; 5843 break; 5844 } 5845 5846 if (!eap->skip) 5847 { 5848 /* store the pattern and compiled regexp program */ 5849 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1, 5850 (int)(arg_end - next_arg - 1))) == NULL) 5851 { 5852 finished = TRUE; 5853 break; 5854 } 5855 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic; 5856 5857 /* Make 'cpoptions' empty, to avoid the 'l' flag */ 5858 cpo_save = p_cpo; 5859 p_cpo = (char_u *)""; 5860 curwin->w_s->b_syn_linecont_prog = 5861 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC); 5862 p_cpo = cpo_save; 5863 #ifdef FEAT_PROFILE 5864 syn_clear_time(&curwin->w_s->b_syn_linecont_time); 5865 #endif 5866 5867 if (curwin->w_s->b_syn_linecont_prog == NULL) 5868 { 5869 vim_free(curwin->w_s->b_syn_linecont_pat); 5870 curwin->w_s->b_syn_linecont_pat = NULL; 5871 finished = TRUE; 5872 break; 5873 } 5874 } 5875 next_arg = skipwhite(arg_end + 1); 5876 } 5877 else 5878 { 5879 eap->arg = next_arg; 5880 if (STRCMP(key, "MATCH") == 0) 5881 syn_cmd_match(eap, TRUE); 5882 else if (STRCMP(key, "REGION") == 0) 5883 syn_cmd_region(eap, TRUE); 5884 else if (STRCMP(key, "CLEAR") == 0) 5885 syn_cmd_clear(eap, TRUE); 5886 else 5887 illegal = TRUE; 5888 finished = TRUE; 5889 break; 5890 } 5891 arg_start = next_arg; 5892 } 5893 vim_free(key); 5894 if (illegal) 5895 EMSG2(_("E404: Illegal arguments: %s"), arg_start); 5896 else if (!finished) 5897 { 5898 eap->nextcmd = check_nextcmd(arg_start); 5899 redraw_curbuf_later(SOME_VALID); 5900 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ 5901 } 5902 } 5903 5904 /* 5905 * Convert a line of highlight group names into a list of group ID numbers. 5906 * "arg" should point to the "contains" or "nextgroup" keyword. 5907 * "arg" is advanced to after the last group name. 5908 * Careful: the argument is modified (NULs added). 5909 * returns FAIL for some error, OK for success. 5910 */ 5911 static int 5912 get_id_list(arg, keylen, list) 5913 char_u **arg; 5914 int keylen; /* length of keyword */ 5915 short **list; /* where to store the resulting list, if not 5916 NULL, the list is silently skipped! */ 5917 { 5918 char_u *p = NULL; 5919 char_u *end; 5920 int round; 5921 int count; 5922 int total_count = 0; 5923 short *retval = NULL; 5924 char_u *name; 5925 regmatch_T regmatch; 5926 int id; 5927 int i; 5928 int failed = FALSE; 5929 5930 /* 5931 * We parse the list twice: 5932 * round == 1: count the number of items, allocate the array. 5933 * round == 2: fill the array with the items. 5934 * In round 1 new groups may be added, causing the number of items to 5935 * grow when a regexp is used. In that case round 1 is done once again. 5936 */ 5937 for (round = 1; round <= 2; ++round) 5938 { 5939 /* 5940 * skip "contains" 5941 */ 5942 p = skipwhite(*arg + keylen); 5943 if (*p != '=') 5944 { 5945 EMSG2(_("E405: Missing equal sign: %s"), *arg); 5946 break; 5947 } 5948 p = skipwhite(p + 1); 5949 if (ends_excmd(*p)) 5950 { 5951 EMSG2(_("E406: Empty argument: %s"), *arg); 5952 break; 5953 } 5954 5955 /* 5956 * parse the arguments after "contains" 5957 */ 5958 count = 0; 5959 while (!ends_excmd(*p)) 5960 { 5961 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end) 5962 ; 5963 name = alloc((int)(end - p + 3)); /* leave room for "^$" */ 5964 if (name == NULL) 5965 { 5966 failed = TRUE; 5967 break; 5968 } 5969 vim_strncpy(name + 1, p, end - p); 5970 if ( STRCMP(name + 1, "ALLBUT") == 0 5971 || STRCMP(name + 1, "ALL") == 0 5972 || STRCMP(name + 1, "TOP") == 0 5973 || STRCMP(name + 1, "CONTAINED") == 0) 5974 { 5975 if (TOUPPER_ASC(**arg) != 'C') 5976 { 5977 EMSG2(_("E407: %s not allowed here"), name + 1); 5978 failed = TRUE; 5979 vim_free(name); 5980 break; 5981 } 5982 if (count != 0) 5983 { 5984 EMSG2(_("E408: %s must be first in contains list"), name + 1); 5985 failed = TRUE; 5986 vim_free(name); 5987 break; 5988 } 5989 if (name[1] == 'A') 5990 id = SYNID_ALLBUT; 5991 else if (name[1] == 'T') 5992 id = SYNID_TOP; 5993 else 5994 id = SYNID_CONTAINED; 5995 id += current_syn_inc_tag; 5996 } 5997 else if (name[1] == '@') 5998 { 5999 id = syn_check_cluster(name + 2, (int)(end - p - 1)); 6000 } 6001 else 6002 { 6003 /* 6004 * Handle full group name. 6005 */ 6006 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL) 6007 id = syn_check_group(name + 1, (int)(end - p)); 6008 else 6009 { 6010 /* 6011 * Handle match of regexp with group names. 6012 */ 6013 *name = '^'; 6014 STRCAT(name, "$"); 6015 regmatch.regprog = vim_regcomp(name, RE_MAGIC); 6016 if (regmatch.regprog == NULL) 6017 { 6018 failed = TRUE; 6019 vim_free(name); 6020 break; 6021 } 6022 6023 regmatch.rm_ic = TRUE; 6024 id = 0; 6025 for (i = highlight_ga.ga_len; --i >= 0; ) 6026 { 6027 if (vim_regexec(®match, HL_TABLE()[i].sg_name, 6028 (colnr_T)0)) 6029 { 6030 if (round == 2) 6031 { 6032 /* Got more items than expected; can happen 6033 * when adding items that match: 6034 * "contains=a.*b,axb". 6035 * Go back to first round */ 6036 if (count >= total_count) 6037 { 6038 vim_free(retval); 6039 round = 1; 6040 } 6041 else 6042 retval[count] = i + 1; 6043 } 6044 ++count; 6045 id = -1; /* remember that we found one */ 6046 } 6047 } 6048 vim_regfree(regmatch.regprog); 6049 } 6050 } 6051 vim_free(name); 6052 if (id == 0) 6053 { 6054 EMSG2(_("E409: Unknown group name: %s"), p); 6055 failed = TRUE; 6056 break; 6057 } 6058 if (id > 0) 6059 { 6060 if (round == 2) 6061 { 6062 /* Got more items than expected, go back to first round */ 6063 if (count >= total_count) 6064 { 6065 vim_free(retval); 6066 round = 1; 6067 } 6068 else 6069 retval[count] = id; 6070 } 6071 ++count; 6072 } 6073 p = skipwhite(end); 6074 if (*p != ',') 6075 break; 6076 p = skipwhite(p + 1); /* skip comma in between arguments */ 6077 } 6078 if (failed) 6079 break; 6080 if (round == 1) 6081 { 6082 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short))); 6083 if (retval == NULL) 6084 break; 6085 retval[count] = 0; /* zero means end of the list */ 6086 total_count = count; 6087 } 6088 } 6089 6090 *arg = p; 6091 if (failed || retval == NULL) 6092 { 6093 vim_free(retval); 6094 return FAIL; 6095 } 6096 6097 if (*list == NULL) 6098 *list = retval; 6099 else 6100 vim_free(retval); /* list already found, don't overwrite it */ 6101 6102 return OK; 6103 } 6104 6105 /* 6106 * Make a copy of an ID list. 6107 */ 6108 static short * 6109 copy_id_list(list) 6110 short *list; 6111 { 6112 int len; 6113 int count; 6114 short *retval; 6115 6116 if (list == NULL) 6117 return NULL; 6118 6119 for (count = 0; list[count]; ++count) 6120 ; 6121 len = (count + 1) * sizeof(short); 6122 retval = (short *)alloc((unsigned)len); 6123 if (retval != NULL) 6124 mch_memmove(retval, list, (size_t)len); 6125 6126 return retval; 6127 } 6128 6129 /* 6130 * Check if syntax group "ssp" is in the ID list "list" of "cur_si". 6131 * "cur_si" can be NULL if not checking the "containedin" list. 6132 * Used to check if a syntax item is in the "contains" or "nextgroup" list of 6133 * the current item. 6134 * This function is called very often, keep it fast!! 6135 */ 6136 static int 6137 in_id_list(cur_si, list, ssp, contained) 6138 stateitem_T *cur_si; /* current item or NULL */ 6139 short *list; /* id list */ 6140 struct sp_syn *ssp; /* group id and ":syn include" tag of group */ 6141 int contained; /* group id is contained */ 6142 { 6143 int retval; 6144 short *scl_list; 6145 short item; 6146 short id = ssp->id; 6147 static int depth = 0; 6148 int r; 6149 6150 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */ 6151 if (cur_si != NULL && ssp->cont_in_list != NULL 6152 && !(cur_si->si_flags & HL_MATCH)) 6153 { 6154 /* Ignore transparent items without a contains argument. Double check 6155 * that we don't go back past the first one. */ 6156 while ((cur_si->si_flags & HL_TRANS_CONT) 6157 && cur_si > (stateitem_T *)(current_state.ga_data)) 6158 --cur_si; 6159 /* cur_si->si_idx is -1 for keywords, these never contain anything. */ 6160 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list, 6161 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn), 6162 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED)) 6163 return TRUE; 6164 } 6165 6166 if (list == NULL) 6167 return FALSE; 6168 6169 /* 6170 * If list is ID_LIST_ALL, we are in a transparent item that isn't 6171 * inside anything. Only allow not-contained groups. 6172 */ 6173 if (list == ID_LIST_ALL) 6174 return !contained; 6175 6176 /* 6177 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the 6178 * contains list. We also require that "id" is at the same ":syn include" 6179 * level as the list. 6180 */ 6181 item = *list; 6182 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER) 6183 { 6184 if (item < SYNID_TOP) 6185 { 6186 /* ALL or ALLBUT: accept all groups in the same file */ 6187 if (item - SYNID_ALLBUT != ssp->inc_tag) 6188 return FALSE; 6189 } 6190 else if (item < SYNID_CONTAINED) 6191 { 6192 /* TOP: accept all not-contained groups in the same file */ 6193 if (item - SYNID_TOP != ssp->inc_tag || contained) 6194 return FALSE; 6195 } 6196 else 6197 { 6198 /* CONTAINED: accept all contained groups in the same file */ 6199 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained) 6200 return FALSE; 6201 } 6202 item = *++list; 6203 retval = FALSE; 6204 } 6205 else 6206 retval = TRUE; 6207 6208 /* 6209 * Return "retval" if id is in the contains list. 6210 */ 6211 while (item != 0) 6212 { 6213 if (item == id) 6214 return retval; 6215 if (item >= SYNID_CLUSTER) 6216 { 6217 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list; 6218 /* restrict recursiveness to 30 to avoid an endless loop for a 6219 * cluster that includes itself (indirectly) */ 6220 if (scl_list != NULL && depth < 30) 6221 { 6222 ++depth; 6223 r = in_id_list(NULL, scl_list, ssp, contained); 6224 --depth; 6225 if (r) 6226 return retval; 6227 } 6228 } 6229 item = *++list; 6230 } 6231 return !retval; 6232 } 6233 6234 struct subcommand 6235 { 6236 char *name; /* subcommand name */ 6237 void (*func)__ARGS((exarg_T *, int)); /* function to call */ 6238 }; 6239 6240 static struct subcommand subcommands[] = 6241 { 6242 {"case", syn_cmd_case}, 6243 {"clear", syn_cmd_clear}, 6244 {"cluster", syn_cmd_cluster}, 6245 {"conceal", syn_cmd_conceal}, 6246 {"enable", syn_cmd_enable}, 6247 {"include", syn_cmd_include}, 6248 {"keyword", syn_cmd_keyword}, 6249 {"list", syn_cmd_list}, 6250 {"manual", syn_cmd_manual}, 6251 {"match", syn_cmd_match}, 6252 {"on", syn_cmd_on}, 6253 {"off", syn_cmd_off}, 6254 {"region", syn_cmd_region}, 6255 {"reset", syn_cmd_reset}, 6256 {"spell", syn_cmd_spell}, 6257 {"sync", syn_cmd_sync}, 6258 {"", syn_cmd_list}, 6259 {NULL, NULL} 6260 }; 6261 6262 /* 6263 * ":syntax". 6264 * This searches the subcommands[] table for the subcommand name, and calls a 6265 * syntax_subcommand() function to do the rest. 6266 */ 6267 void 6268 ex_syntax(eap) 6269 exarg_T *eap; 6270 { 6271 char_u *arg = eap->arg; 6272 char_u *subcmd_end; 6273 char_u *subcmd_name; 6274 int i; 6275 6276 syn_cmdlinep = eap->cmdlinep; 6277 6278 /* isolate subcommand name */ 6279 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end) 6280 ; 6281 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg)); 6282 if (subcmd_name != NULL) 6283 { 6284 if (eap->skip) /* skip error messages for all subcommands */ 6285 ++emsg_skip; 6286 for (i = 0; ; ++i) 6287 { 6288 if (subcommands[i].name == NULL) 6289 { 6290 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name); 6291 break; 6292 } 6293 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0) 6294 { 6295 eap->arg = skipwhite(subcmd_end); 6296 (subcommands[i].func)(eap, FALSE); 6297 break; 6298 } 6299 } 6300 vim_free(subcmd_name); 6301 if (eap->skip) 6302 --emsg_skip; 6303 } 6304 } 6305 6306 void 6307 ex_ownsyntax(eap) 6308 exarg_T *eap; 6309 { 6310 char_u *old_value; 6311 char_u *new_value; 6312 6313 if (curwin->w_s == &curwin->w_buffer->b_s) 6314 { 6315 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T)); 6316 memset(curwin->w_s, 0, sizeof(synblock_T)); 6317 hash_init(&curwin->w_s->b_keywtab); 6318 hash_init(&curwin->w_s->b_keywtab_ic); 6319 #ifdef FEAT_SPELL 6320 /* TODO: keep the spell checking as it was. */ 6321 curwin->w_p_spell = FALSE; /* No spell checking */ 6322 clear_string_option(&curwin->w_s->b_p_spc); 6323 clear_string_option(&curwin->w_s->b_p_spf); 6324 clear_string_option(&curwin->w_s->b_p_spl); 6325 #endif 6326 } 6327 6328 /* save value of b:current_syntax */ 6329 old_value = get_var_value((char_u *)"b:current_syntax"); 6330 if (old_value != NULL) 6331 old_value = vim_strsave(old_value); 6332 6333 /* Apply the "syntax" autocommand event, this finds and loads the syntax 6334 * file. */ 6335 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf); 6336 6337 /* move value of b:current_syntax to w:current_syntax */ 6338 new_value = get_var_value((char_u *)"b:current_syntax"); 6339 if (new_value != NULL) 6340 set_internal_string_var((char_u *)"w:current_syntax", new_value); 6341 6342 /* restore value of b:current_syntax */ 6343 if (old_value == NULL) 6344 do_unlet((char_u *)"b:current_syntax", TRUE); 6345 else 6346 { 6347 set_internal_string_var((char_u *)"b:current_syntax", old_value); 6348 vim_free(old_value); 6349 } 6350 } 6351 6352 int 6353 syntax_present(win) 6354 win_T *win; 6355 { 6356 return (win->w_s->b_syn_patterns.ga_len != 0 6357 || win->w_s->b_syn_clusters.ga_len != 0 6358 || win->w_s->b_keywtab.ht_used > 0 6359 || win->w_s->b_keywtab_ic.ht_used > 0); 6360 } 6361 6362 #if defined(FEAT_CMDL_COMPL) || defined(PROTO) 6363 6364 static enum 6365 { 6366 EXP_SUBCMD, /* expand ":syn" sub-commands */ 6367 EXP_CASE /* expand ":syn case" arguments */ 6368 } expand_what; 6369 6370 /* 6371 * Reset include_link, include_default, include_none to 0. 6372 * Called when we are done expanding. 6373 */ 6374 void 6375 reset_expand_highlight() 6376 { 6377 include_link = include_default = include_none = 0; 6378 } 6379 6380 /* 6381 * Handle command line completion for :match and :echohl command: Add "None" 6382 * as highlight group. 6383 */ 6384 void 6385 set_context_in_echohl_cmd(xp, arg) 6386 expand_T *xp; 6387 char_u *arg; 6388 { 6389 xp->xp_context = EXPAND_HIGHLIGHT; 6390 xp->xp_pattern = arg; 6391 include_none = 1; 6392 } 6393 6394 /* 6395 * Handle command line completion for :syntax command. 6396 */ 6397 void 6398 set_context_in_syntax_cmd(xp, arg) 6399 expand_T *xp; 6400 char_u *arg; 6401 { 6402 char_u *p; 6403 6404 /* Default: expand subcommands */ 6405 xp->xp_context = EXPAND_SYNTAX; 6406 expand_what = EXP_SUBCMD; 6407 xp->xp_pattern = arg; 6408 include_link = 0; 6409 include_default = 0; 6410 6411 /* (part of) subcommand already typed */ 6412 if (*arg != NUL) 6413 { 6414 p = skiptowhite(arg); 6415 if (*p != NUL) /* past first word */ 6416 { 6417 xp->xp_pattern = skipwhite(p); 6418 if (*skiptowhite(xp->xp_pattern) != NUL) 6419 xp->xp_context = EXPAND_NOTHING; 6420 else if (STRNICMP(arg, "case", p - arg) == 0) 6421 expand_what = EXP_CASE; 6422 else if ( STRNICMP(arg, "keyword", p - arg) == 0 6423 || STRNICMP(arg, "region", p - arg) == 0 6424 || STRNICMP(arg, "match", p - arg) == 0 6425 || STRNICMP(arg, "list", p - arg) == 0) 6426 xp->xp_context = EXPAND_HIGHLIGHT; 6427 else 6428 xp->xp_context = EXPAND_NOTHING; 6429 } 6430 } 6431 } 6432 6433 static char *(case_args[]) = {"match", "ignore", NULL}; 6434 6435 /* 6436 * Function given to ExpandGeneric() to obtain the list syntax names for 6437 * expansion. 6438 */ 6439 char_u * 6440 get_syntax_name(xp, idx) 6441 expand_T *xp UNUSED; 6442 int idx; 6443 { 6444 if (expand_what == EXP_SUBCMD) 6445 return (char_u *)subcommands[idx].name; 6446 return (char_u *)case_args[idx]; 6447 } 6448 6449 #endif /* FEAT_CMDL_COMPL */ 6450 6451 /* 6452 * Function called for expression evaluation: get syntax ID at file position. 6453 */ 6454 int 6455 syn_get_id(wp, lnum, col, trans, spellp, keep_state) 6456 win_T *wp; 6457 long lnum; 6458 colnr_T col; 6459 int trans; /* remove transparency */ 6460 int *spellp; /* return: can do spell checking */ 6461 int keep_state; /* keep state of char at "col" */ 6462 { 6463 /* When the position is not after the current position and in the same 6464 * line of the same buffer, need to restart parsing. */ 6465 if (wp->w_buffer != syn_buf 6466 || lnum != current_lnum 6467 || col < current_col) 6468 syntax_start(wp, lnum); 6469 6470 (void)get_syntax_attr(col, spellp, keep_state); 6471 6472 return (trans ? current_trans_id : current_id); 6473 } 6474 6475 #if defined(FEAT_CONCEAL) || defined(PROTO) 6476 /* 6477 * Get extra information about the syntax item. Must be called right after 6478 * get_syntax_attr(). 6479 * Stores the current item sequence nr in "*seqnrp". 6480 * Returns the current flags. 6481 */ 6482 int 6483 get_syntax_info(seqnrp) 6484 int *seqnrp; 6485 { 6486 *seqnrp = current_seqnr; 6487 return current_flags; 6488 } 6489 6490 /* 6491 * Return conceal substitution character 6492 */ 6493 int 6494 syn_get_sub_char() 6495 { 6496 return current_sub_char; 6497 } 6498 #endif 6499 6500 #if defined(FEAT_EVAL) || defined(PROTO) 6501 /* 6502 * Return the syntax ID at position "i" in the current stack. 6503 * The caller must have called syn_get_id() before to fill the stack. 6504 * Returns -1 when "i" is out of range. 6505 */ 6506 int 6507 syn_get_stack_item(i) 6508 int i; 6509 { 6510 if (i >= current_state.ga_len) 6511 { 6512 /* Need to invalidate the state, because we didn't properly finish it 6513 * for the last character, "keep_state" was TRUE. */ 6514 invalidate_current_state(); 6515 current_col = MAXCOL; 6516 return -1; 6517 } 6518 return CUR_STATE(i).si_id; 6519 } 6520 #endif 6521 6522 #if defined(FEAT_FOLDING) || defined(PROTO) 6523 /* 6524 * Function called to get folding level for line "lnum" in window "wp". 6525 */ 6526 int 6527 syn_get_foldlevel(wp, lnum) 6528 win_T *wp; 6529 long lnum; 6530 { 6531 int level = 0; 6532 int i; 6533 6534 /* Return quickly when there are no fold items at all. */ 6535 if (wp->w_s->b_syn_folditems != 0) 6536 { 6537 syntax_start(wp, lnum); 6538 6539 for (i = 0; i < current_state.ga_len; ++i) 6540 if (CUR_STATE(i).si_flags & HL_FOLD) 6541 ++level; 6542 } 6543 if (level > wp->w_p_fdn) 6544 { 6545 level = wp->w_p_fdn; 6546 if (level < 0) 6547 level = 0; 6548 } 6549 return level; 6550 } 6551 #endif 6552 6553 #if defined(FEAT_PROFILE) || defined(PROTO) 6554 /* 6555 * ":syntime". 6556 */ 6557 void 6558 ex_syntime(eap) 6559 exarg_T *eap; 6560 { 6561 if (STRCMP(eap->arg, "on") == 0) 6562 syn_time_on = TRUE; 6563 else if (STRCMP(eap->arg, "off") == 0) 6564 syn_time_on = FALSE; 6565 else if (STRCMP(eap->arg, "clear") == 0) 6566 syntime_clear(); 6567 else if (STRCMP(eap->arg, "report") == 0) 6568 syntime_report(); 6569 else 6570 EMSG2(_(e_invarg2), eap->arg); 6571 } 6572 6573 static void 6574 syn_clear_time(st) 6575 syn_time_T *st; 6576 { 6577 profile_zero(&st->total); 6578 profile_zero(&st->slowest); 6579 st->count = 0; 6580 st->match = 0; 6581 } 6582 6583 /* 6584 * Clear the syntax timing for the current buffer. 6585 */ 6586 static void 6587 syntime_clear() 6588 { 6589 int idx; 6590 synpat_T *spp; 6591 6592 if (!syntax_present(curwin)) 6593 { 6594 MSG(_(msg_no_items)); 6595 return; 6596 } 6597 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx) 6598 { 6599 spp = &(SYN_ITEMS(curwin->w_s)[idx]); 6600 syn_clear_time(&spp->sp_time); 6601 } 6602 } 6603 6604 #if defined(FEAT_CMDL_COMPL) || defined(PROTO) 6605 /* 6606 * Function given to ExpandGeneric() to obtain the possible arguments of the 6607 * ":syntime {on,off,clear,report}" command. 6608 */ 6609 char_u * 6610 get_syntime_arg(xp, idx) 6611 expand_T *xp UNUSED; 6612 int idx; 6613 { 6614 switch (idx) 6615 { 6616 case 0: return (char_u *)"on"; 6617 case 1: return (char_u *)"off"; 6618 case 2: return (char_u *)"clear"; 6619 case 3: return (char_u *)"report"; 6620 } 6621 return NULL; 6622 } 6623 #endif 6624 6625 typedef struct 6626 { 6627 proftime_T total; 6628 int count; 6629 int match; 6630 proftime_T slowest; 6631 proftime_T average; 6632 int id; 6633 char_u *pattern; 6634 } time_entry_T; 6635 6636 static int 6637 #ifdef __BORLANDC__ 6638 _RTLENTRYF 6639 #endif 6640 syn_compare_syntime(v1, v2) 6641 const void *v1; 6642 const void *v2; 6643 { 6644 const time_entry_T *s1 = v1; 6645 const time_entry_T *s2 = v2; 6646 6647 return profile_cmp(&s1->total, &s2->total); 6648 } 6649 6650 /* 6651 * Clear the syntax timing for the current buffer. 6652 */ 6653 static void 6654 syntime_report() 6655 { 6656 int idx; 6657 synpat_T *spp; 6658 # ifdef FEAT_FLOAT 6659 proftime_T tm; 6660 # endif 6661 int len; 6662 proftime_T total_total; 6663 int total_count = 0; 6664 garray_T ga; 6665 time_entry_T *p; 6666 6667 if (!syntax_present(curwin)) 6668 { 6669 MSG(_(msg_no_items)); 6670 return; 6671 } 6672 6673 ga_init2(&ga, sizeof(time_entry_T), 50); 6674 profile_zero(&total_total); 6675 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx) 6676 { 6677 spp = &(SYN_ITEMS(curwin->w_s)[idx]); 6678 if (spp->sp_time.count > 0) 6679 { 6680 (void)ga_grow(&ga, 1); 6681 p = ((time_entry_T *)ga.ga_data) + ga.ga_len; 6682 p->total = spp->sp_time.total; 6683 profile_add(&total_total, &spp->sp_time.total); 6684 p->count = spp->sp_time.count; 6685 p->match = spp->sp_time.match; 6686 total_count += spp->sp_time.count; 6687 p->slowest = spp->sp_time.slowest; 6688 # ifdef FEAT_FLOAT 6689 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm); 6690 p->average = tm; 6691 # endif 6692 p->id = spp->sp_syn.id; 6693 p->pattern = spp->sp_pattern; 6694 ++ga.ga_len; 6695 } 6696 } 6697 6698 /* sort on total time */ 6699 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T), 6700 syn_compare_syntime); 6701 6702 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN")); 6703 MSG_PUTS("\n"); 6704 for (idx = 0; idx < ga.ga_len && !got_int; ++idx) 6705 { 6706 spp = &(SYN_ITEMS(curwin->w_s)[idx]); 6707 p = ((time_entry_T *)ga.ga_data) + idx; 6708 6709 MSG_PUTS(profile_msg(&p->total)); 6710 MSG_PUTS(" "); /* make sure there is always a separating space */ 6711 msg_advance(13); 6712 msg_outnum(p->count); 6713 MSG_PUTS(" "); 6714 msg_advance(20); 6715 msg_outnum(p->match); 6716 MSG_PUTS(" "); 6717 msg_advance(26); 6718 MSG_PUTS(profile_msg(&p->slowest)); 6719 MSG_PUTS(" "); 6720 msg_advance(38); 6721 # ifdef FEAT_FLOAT 6722 MSG_PUTS(profile_msg(&p->average)); 6723 MSG_PUTS(" "); 6724 # endif 6725 msg_advance(50); 6726 msg_outtrans(HL_TABLE()[p->id - 1].sg_name); 6727 MSG_PUTS(" "); 6728 6729 msg_advance(69); 6730 if (Columns < 80) 6731 len = 20; /* will wrap anyway */ 6732 else 6733 len = Columns - 70; 6734 if (len > (int)STRLEN(p->pattern)) 6735 len = (int)STRLEN(p->pattern); 6736 msg_outtrans_len(p->pattern, len); 6737 MSG_PUTS("\n"); 6738 } 6739 ga_clear(&ga); 6740 if (!got_int) 6741 { 6742 MSG_PUTS("\n"); 6743 MSG_PUTS(profile_msg(&total_total)); 6744 msg_advance(13); 6745 msg_outnum(total_count); 6746 MSG_PUTS("\n"); 6747 } 6748 } 6749 #endif 6750 6751 #endif /* FEAT_SYN_HL */ 6752 6753 /************************************** 6754 * Highlighting stuff * 6755 **************************************/ 6756 6757 /* 6758 * The default highlight groups. These are compiled-in for fast startup and 6759 * they still work when the runtime files can't be found. 6760 * When making changes here, also change runtime/colors/default.vim! 6761 * The #ifdefs are needed to reduce the amount of static data. Helps to make 6762 * the 16 bit DOS (museum) version compile. 6763 */ 6764 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 6765 # define CENT(a, b) b 6766 #else 6767 # define CENT(a, b) a 6768 #endif 6769 static char *(highlight_init_both[]) = 6770 { 6771 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White", 6772 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"), 6773 CENT("IncSearch term=reverse cterm=reverse", 6774 "IncSearch term=reverse cterm=reverse gui=reverse"), 6775 CENT("ModeMsg term=bold cterm=bold", 6776 "ModeMsg term=bold cterm=bold gui=bold"), 6777 CENT("NonText term=bold ctermfg=Blue", 6778 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"), 6779 CENT("StatusLine term=reverse,bold cterm=reverse,bold", 6780 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"), 6781 CENT("StatusLineNC term=reverse cterm=reverse", 6782 "StatusLineNC term=reverse cterm=reverse gui=reverse"), 6783 #ifdef FEAT_VERTSPLIT 6784 CENT("VertSplit term=reverse cterm=reverse", 6785 "VertSplit term=reverse cterm=reverse gui=reverse"), 6786 #endif 6787 #ifdef FEAT_CLIPBOARD 6788 CENT("VisualNOS term=underline,bold cterm=underline,bold", 6789 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"), 6790 #endif 6791 #ifdef FEAT_DIFF 6792 CENT("DiffText term=reverse cterm=bold ctermbg=Red", 6793 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"), 6794 #endif 6795 #ifdef FEAT_INS_EXPAND 6796 CENT("PmenuSbar ctermbg=Grey", 6797 "PmenuSbar ctermbg=Grey guibg=Grey"), 6798 #endif 6799 #ifdef FEAT_WINDOWS 6800 CENT("TabLineSel term=bold cterm=bold", 6801 "TabLineSel term=bold cterm=bold gui=bold"), 6802 CENT("TabLineFill term=reverse cterm=reverse", 6803 "TabLineFill term=reverse cterm=reverse gui=reverse"), 6804 #endif 6805 #ifdef FEAT_GUI 6806 "Cursor guibg=fg guifg=bg", 6807 "lCursor guibg=fg guifg=bg", /* should be different, but what? */ 6808 #endif 6809 NULL 6810 }; 6811 6812 static char *(highlight_init_light[]) = 6813 { 6814 CENT("Directory term=bold ctermfg=DarkBlue", 6815 "Directory term=bold ctermfg=DarkBlue guifg=Blue"), 6816 CENT("LineNr term=underline ctermfg=Brown", 6817 "LineNr term=underline ctermfg=Brown guifg=Brown"), 6818 CENT("CursorLineNr term=bold ctermfg=Brown", 6819 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"), 6820 CENT("MoreMsg term=bold ctermfg=DarkGreen", 6821 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"), 6822 CENT("Question term=standout ctermfg=DarkGreen", 6823 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"), 6824 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE", 6825 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"), 6826 #ifdef FEAT_SPELL 6827 CENT("SpellBad term=reverse ctermbg=LightRed", 6828 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"), 6829 CENT("SpellCap term=reverse ctermbg=LightBlue", 6830 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"), 6831 CENT("SpellRare term=reverse ctermbg=LightMagenta", 6832 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"), 6833 CENT("SpellLocal term=underline ctermbg=Cyan", 6834 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"), 6835 #endif 6836 #ifdef FEAT_INS_EXPAND 6837 CENT("PmenuThumb ctermbg=Black", 6838 "PmenuThumb ctermbg=Black guibg=Black"), 6839 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black", 6840 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"), 6841 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black", 6842 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"), 6843 #endif 6844 CENT("SpecialKey term=bold ctermfg=DarkBlue", 6845 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"), 6846 CENT("Title term=bold ctermfg=DarkMagenta", 6847 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"), 6848 CENT("WarningMsg term=standout ctermfg=DarkRed", 6849 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"), 6850 #ifdef FEAT_WILDMENU 6851 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black", 6852 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"), 6853 #endif 6854 #ifdef FEAT_FOLDING 6855 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue", 6856 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"), 6857 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue", 6858 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"), 6859 #endif 6860 #ifdef FEAT_SIGNS 6861 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue", 6862 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"), 6863 #endif 6864 CENT("Visual term=reverse", 6865 "Visual term=reverse guibg=LightGrey"), 6866 #ifdef FEAT_DIFF 6867 CENT("DiffAdd term=bold ctermbg=LightBlue", 6868 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"), 6869 CENT("DiffChange term=bold ctermbg=LightMagenta", 6870 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"), 6871 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan", 6872 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"), 6873 #endif 6874 #ifdef FEAT_WINDOWS 6875 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey", 6876 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"), 6877 #endif 6878 #ifdef FEAT_SYN_HL 6879 CENT("CursorColumn term=reverse ctermbg=LightGrey", 6880 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"), 6881 CENT("CursorLine term=underline cterm=underline", 6882 "CursorLine term=underline cterm=underline guibg=Grey90"), 6883 CENT("ColorColumn term=reverse ctermbg=LightRed", 6884 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"), 6885 #endif 6886 #ifdef FEAT_CONCEAL 6887 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey", 6888 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"), 6889 #endif 6890 #ifdef FEAT_AUTOCMD 6891 CENT("MatchParen term=reverse ctermbg=Cyan", 6892 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"), 6893 #endif 6894 #ifdef FEAT_GUI 6895 "Normal gui=NONE", 6896 #endif 6897 NULL 6898 }; 6899 6900 static char *(highlight_init_dark[]) = 6901 { 6902 CENT("Directory term=bold ctermfg=LightCyan", 6903 "Directory term=bold ctermfg=LightCyan guifg=Cyan"), 6904 CENT("LineNr term=underline ctermfg=Yellow", 6905 "LineNr term=underline ctermfg=Yellow guifg=Yellow"), 6906 CENT("CursorLineNr term=bold ctermfg=Yellow", 6907 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"), 6908 CENT("MoreMsg term=bold ctermfg=LightGreen", 6909 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"), 6910 CENT("Question term=standout ctermfg=LightGreen", 6911 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"), 6912 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black", 6913 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"), 6914 CENT("SpecialKey term=bold ctermfg=LightBlue", 6915 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"), 6916 #ifdef FEAT_SPELL 6917 CENT("SpellBad term=reverse ctermbg=Red", 6918 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"), 6919 CENT("SpellCap term=reverse ctermbg=Blue", 6920 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"), 6921 CENT("SpellRare term=reverse ctermbg=Magenta", 6922 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"), 6923 CENT("SpellLocal term=underline ctermbg=Cyan", 6924 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"), 6925 #endif 6926 #ifdef FEAT_INS_EXPAND 6927 CENT("PmenuThumb ctermbg=White", 6928 "PmenuThumb ctermbg=White guibg=White"), 6929 CENT("Pmenu ctermbg=Magenta ctermfg=Black", 6930 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"), 6931 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey", 6932 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"), 6933 #endif 6934 CENT("Title term=bold ctermfg=LightMagenta", 6935 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"), 6936 CENT("WarningMsg term=standout ctermfg=LightRed", 6937 "WarningMsg term=standout ctermfg=LightRed guifg=Red"), 6938 #ifdef FEAT_WILDMENU 6939 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black", 6940 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"), 6941 #endif 6942 #ifdef FEAT_FOLDING 6943 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan", 6944 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"), 6945 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan", 6946 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"), 6947 #endif 6948 #ifdef FEAT_SIGNS 6949 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan", 6950 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"), 6951 #endif 6952 CENT("Visual term=reverse", 6953 "Visual term=reverse guibg=DarkGrey"), 6954 #ifdef FEAT_DIFF 6955 CENT("DiffAdd term=bold ctermbg=DarkBlue", 6956 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"), 6957 CENT("DiffChange term=bold ctermbg=DarkMagenta", 6958 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"), 6959 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan", 6960 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"), 6961 #endif 6962 #ifdef FEAT_WINDOWS 6963 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey", 6964 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"), 6965 #endif 6966 #ifdef FEAT_SYN_HL 6967 CENT("CursorColumn term=reverse ctermbg=DarkGrey", 6968 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"), 6969 CENT("CursorLine term=underline cterm=underline", 6970 "CursorLine term=underline cterm=underline guibg=Grey40"), 6971 CENT("ColorColumn term=reverse ctermbg=DarkRed", 6972 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"), 6973 #endif 6974 #ifdef FEAT_AUTOCMD 6975 CENT("MatchParen term=reverse ctermbg=DarkCyan", 6976 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"), 6977 #endif 6978 #ifdef FEAT_CONCEAL 6979 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey", 6980 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"), 6981 #endif 6982 #ifdef FEAT_GUI 6983 "Normal gui=NONE", 6984 #endif 6985 NULL 6986 }; 6987 6988 void 6989 init_highlight(both, reset) 6990 int both; /* include groups where 'bg' doesn't matter */ 6991 int reset; /* clear group first */ 6992 { 6993 int i; 6994 char **pp; 6995 static int had_both = FALSE; 6996 #ifdef FEAT_EVAL 6997 char_u *p; 6998 6999 /* 7000 * Try finding the color scheme file. Used when a color file was loaded 7001 * and 'background' or 't_Co' is changed. 7002 */ 7003 p = get_var_value((char_u *)"g:colors_name"); 7004 if (p != NULL) 7005 { 7006 /* The value of g:colors_name could be freed when sourcing the script, 7007 * making "p" invalid, so copy it. */ 7008 char_u *copy_p = vim_strsave(p); 7009 int r; 7010 7011 if (copy_p != NULL) 7012 { 7013 r = load_colors(copy_p); 7014 vim_free(copy_p); 7015 if (r == OK) 7016 return; 7017 } 7018 } 7019 7020 #endif 7021 7022 /* 7023 * Didn't use a color file, use the compiled-in colors. 7024 */ 7025 if (both) 7026 { 7027 had_both = TRUE; 7028 pp = highlight_init_both; 7029 for (i = 0; pp[i] != NULL; ++i) 7030 do_highlight((char_u *)pp[i], reset, TRUE); 7031 } 7032 else if (!had_both) 7033 /* Don't do anything before the call with both == TRUE from main(). 7034 * Not everything has been setup then, and that call will overrule 7035 * everything anyway. */ 7036 return; 7037 7038 if (*p_bg == 'l') 7039 pp = highlight_init_light; 7040 else 7041 pp = highlight_init_dark; 7042 for (i = 0; pp[i] != NULL; ++i) 7043 do_highlight((char_u *)pp[i], reset, TRUE); 7044 7045 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it 7046 * depend on the number of colors available. 7047 * With 8 colors brown is equal to yellow, need to use black for Search fg 7048 * to avoid Statement highlighted text disappears. 7049 * Clear the attributes, needed when changing the t_Co value. */ 7050 if (t_colors > 8) 7051 do_highlight((char_u *)(*p_bg == 'l' 7052 ? "Visual cterm=NONE ctermbg=LightGrey" 7053 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE); 7054 else 7055 { 7056 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE", 7057 FALSE, TRUE); 7058 if (*p_bg == 'l') 7059 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE); 7060 } 7061 7062 #ifdef FEAT_SYN_HL 7063 /* 7064 * If syntax highlighting is enabled load the highlighting for it. 7065 */ 7066 if (get_var_value((char_u *)"g:syntax_on") != NULL) 7067 { 7068 static int recursive = 0; 7069 7070 if (recursive >= 5) 7071 EMSG(_("E679: recursive loop loading syncolor.vim")); 7072 else 7073 { 7074 ++recursive; 7075 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE); 7076 --recursive; 7077 } 7078 } 7079 #endif 7080 } 7081 7082 /* 7083 * Load color file "name". 7084 * Return OK for success, FAIL for failure. 7085 */ 7086 int 7087 load_colors(name) 7088 char_u *name; 7089 { 7090 char_u *buf; 7091 int retval = FAIL; 7092 static int recursive = FALSE; 7093 7094 /* When being called recursively, this is probably because setting 7095 * 'background' caused the highlighting to be reloaded. This means it is 7096 * working, thus we should return OK. */ 7097 if (recursive) 7098 return OK; 7099 7100 recursive = TRUE; 7101 buf = alloc((unsigned)(STRLEN(name) + 12)); 7102 if (buf != NULL) 7103 { 7104 sprintf((char *)buf, "colors/%s.vim", name); 7105 retval = source_runtime(buf, FALSE); 7106 vim_free(buf); 7107 #ifdef FEAT_AUTOCMD 7108 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf); 7109 #endif 7110 } 7111 recursive = FALSE; 7112 7113 return retval; 7114 } 7115 7116 /* 7117 * Handle the ":highlight .." command. 7118 * When using ":hi clear" this is called recursively for each group with 7119 * "forceit" and "init" both TRUE. 7120 */ 7121 void 7122 do_highlight(line, forceit, init) 7123 char_u *line; 7124 int forceit; 7125 int init; /* TRUE when called for initializing */ 7126 { 7127 char_u *name_end; 7128 char_u *p; 7129 char_u *linep; 7130 char_u *key_start; 7131 char_u *arg_start; 7132 char_u *key = NULL, *arg = NULL; 7133 long i; 7134 int off; 7135 int len; 7136 int attr; 7137 int id; 7138 int idx; 7139 int dodefault = FALSE; 7140 int doclear = FALSE; 7141 int dolink = FALSE; 7142 int error = FALSE; 7143 int color; 7144 int is_normal_group = FALSE; /* "Normal" group */ 7145 #ifdef FEAT_GUI_X11 7146 int is_menu_group = FALSE; /* "Menu" group */ 7147 int is_scrollbar_group = FALSE; /* "Scrollbar" group */ 7148 int is_tooltip_group = FALSE; /* "Tooltip" group */ 7149 int do_colors = FALSE; /* need to update colors? */ 7150 #else 7151 # define is_menu_group 0 7152 # define is_tooltip_group 0 7153 #endif 7154 7155 /* 7156 * If no argument, list current highlighting. 7157 */ 7158 if (ends_excmd(*line)) 7159 { 7160 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i) 7161 /* TODO: only call when the group has attributes set */ 7162 highlight_list_one((int)i); 7163 return; 7164 } 7165 7166 /* 7167 * Isolate the name. 7168 */ 7169 name_end = skiptowhite(line); 7170 linep = skipwhite(name_end); 7171 7172 /* 7173 * Check for "default" argument. 7174 */ 7175 if (STRNCMP(line, "default", name_end - line) == 0) 7176 { 7177 dodefault = TRUE; 7178 line = linep; 7179 name_end = skiptowhite(line); 7180 linep = skipwhite(name_end); 7181 } 7182 7183 /* 7184 * Check for "clear" or "link" argument. 7185 */ 7186 if (STRNCMP(line, "clear", name_end - line) == 0) 7187 doclear = TRUE; 7188 if (STRNCMP(line, "link", name_end - line) == 0) 7189 dolink = TRUE; 7190 7191 /* 7192 * ":highlight {group-name}": list highlighting for one group. 7193 */ 7194 if (!doclear && !dolink && ends_excmd(*linep)) 7195 { 7196 id = syn_namen2id(line, (int)(name_end - line)); 7197 if (id == 0) 7198 EMSG2(_("E411: highlight group not found: %s"), line); 7199 else 7200 highlight_list_one(id); 7201 return; 7202 } 7203 7204 /* 7205 * Handle ":highlight link {from} {to}" command. 7206 */ 7207 if (dolink) 7208 { 7209 char_u *from_start = linep; 7210 char_u *from_end; 7211 char_u *to_start; 7212 char_u *to_end; 7213 int from_id; 7214 int to_id; 7215 7216 from_end = skiptowhite(from_start); 7217 to_start = skipwhite(from_end); 7218 to_end = skiptowhite(to_start); 7219 7220 if (ends_excmd(*from_start) || ends_excmd(*to_start)) 7221 { 7222 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""), 7223 from_start); 7224 return; 7225 } 7226 7227 if (!ends_excmd(*skipwhite(to_end))) 7228 { 7229 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start); 7230 return; 7231 } 7232 7233 from_id = syn_check_group(from_start, (int)(from_end - from_start)); 7234 if (STRNCMP(to_start, "NONE", 4) == 0) 7235 to_id = 0; 7236 else 7237 to_id = syn_check_group(to_start, (int)(to_end - to_start)); 7238 7239 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0)) 7240 { 7241 /* 7242 * Don't allow a link when there already is some highlighting 7243 * for the group, unless '!' is used 7244 */ 7245 if (to_id > 0 && !forceit && !init 7246 && hl_has_settings(from_id - 1, dodefault)) 7247 { 7248 if (sourcing_name == NULL && !dodefault) 7249 EMSG(_("E414: group has settings, highlight link ignored")); 7250 } 7251 else 7252 { 7253 if (!init) 7254 HL_TABLE()[from_id - 1].sg_set |= SG_LINK; 7255 HL_TABLE()[from_id - 1].sg_link = to_id; 7256 #ifdef FEAT_EVAL 7257 HL_TABLE()[from_id - 1].sg_scriptID = current_SID; 7258 #endif 7259 redraw_all_later(SOME_VALID); 7260 } 7261 } 7262 7263 /* Only call highlight_changed() once, after sourcing a syntax file */ 7264 need_highlight_changed = TRUE; 7265 7266 return; 7267 } 7268 7269 if (doclear) 7270 { 7271 /* 7272 * ":highlight clear [group]" command. 7273 */ 7274 line = linep; 7275 if (ends_excmd(*line)) 7276 { 7277 #ifdef FEAT_GUI 7278 /* First, we do not destroy the old values, but allocate the new 7279 * ones and update the display. THEN we destroy the old values. 7280 * If we destroy the old values first, then the old values 7281 * (such as GuiFont's or GuiFontset's) will still be displayed but 7282 * invalid because they were free'd. 7283 */ 7284 if (gui.in_use) 7285 { 7286 # ifdef FEAT_BEVAL_TIP 7287 gui_init_tooltip_font(); 7288 # endif 7289 # if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF)) 7290 gui_init_menu_font(); 7291 # endif 7292 } 7293 # if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11) 7294 gui_mch_def_colors(); 7295 # endif 7296 # ifdef FEAT_GUI_X11 7297 # ifdef FEAT_MENU 7298 7299 /* This only needs to be done when there is no Menu highlight 7300 * group defined by default, which IS currently the case. 7301 */ 7302 gui_mch_new_menu_colors(); 7303 # endif 7304 if (gui.in_use) 7305 { 7306 gui_new_scrollbar_colors(); 7307 # ifdef FEAT_BEVAL 7308 gui_mch_new_tooltip_colors(); 7309 # endif 7310 # ifdef FEAT_MENU 7311 gui_mch_new_menu_font(); 7312 # endif 7313 } 7314 # endif 7315 7316 /* Ok, we're done allocating the new default graphics items. 7317 * The screen should already be refreshed at this point. 7318 * It is now Ok to clear out the old data. 7319 */ 7320 #endif 7321 #ifdef FEAT_EVAL 7322 do_unlet((char_u *)"colors_name", TRUE); 7323 #endif 7324 restore_cterm_colors(); 7325 7326 /* 7327 * Clear all default highlight groups and load the defaults. 7328 */ 7329 for (idx = 0; idx < highlight_ga.ga_len; ++idx) 7330 highlight_clear(idx); 7331 init_highlight(TRUE, TRUE); 7332 #ifdef FEAT_GUI 7333 if (gui.in_use) 7334 highlight_gui_started(); 7335 #endif 7336 highlight_changed(); 7337 redraw_later_clear(); 7338 return; 7339 } 7340 name_end = skiptowhite(line); 7341 linep = skipwhite(name_end); 7342 } 7343 7344 /* 7345 * Find the group name in the table. If it does not exist yet, add it. 7346 */ 7347 id = syn_check_group(line, (int)(name_end - line)); 7348 if (id == 0) /* failed (out of memory) */ 7349 return; 7350 idx = id - 1; /* index is ID minus one */ 7351 7352 /* Return if "default" was used and the group already has settings. */ 7353 if (dodefault && hl_has_settings(idx, TRUE)) 7354 return; 7355 7356 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0) 7357 is_normal_group = TRUE; 7358 #ifdef FEAT_GUI_X11 7359 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0) 7360 is_menu_group = TRUE; 7361 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0) 7362 is_scrollbar_group = TRUE; 7363 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0) 7364 is_tooltip_group = TRUE; 7365 #endif 7366 7367 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */ 7368 if (doclear || (forceit && init)) 7369 { 7370 highlight_clear(idx); 7371 if (!doclear) 7372 HL_TABLE()[idx].sg_set = 0; 7373 } 7374 7375 if (!doclear) 7376 while (!ends_excmd(*linep)) 7377 { 7378 key_start = linep; 7379 if (*linep == '=') 7380 { 7381 EMSG2(_("E415: unexpected equal sign: %s"), key_start); 7382 error = TRUE; 7383 break; 7384 } 7385 7386 /* 7387 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or 7388 * "guibg"). 7389 */ 7390 while (*linep && !vim_iswhite(*linep) && *linep != '=') 7391 ++linep; 7392 vim_free(key); 7393 key = vim_strnsave_up(key_start, (int)(linep - key_start)); 7394 if (key == NULL) 7395 { 7396 error = TRUE; 7397 break; 7398 } 7399 linep = skipwhite(linep); 7400 7401 if (STRCMP(key, "NONE") == 0) 7402 { 7403 if (!init || HL_TABLE()[idx].sg_set == 0) 7404 { 7405 if (!init) 7406 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI; 7407 highlight_clear(idx); 7408 } 7409 continue; 7410 } 7411 7412 /* 7413 * Check for the equal sign. 7414 */ 7415 if (*linep != '=') 7416 { 7417 EMSG2(_("E416: missing equal sign: %s"), key_start); 7418 error = TRUE; 7419 break; 7420 } 7421 ++linep; 7422 7423 /* 7424 * Isolate the argument. 7425 */ 7426 linep = skipwhite(linep); 7427 if (*linep == '\'') /* guifg='color name' */ 7428 { 7429 arg_start = ++linep; 7430 linep = vim_strchr(linep, '\''); 7431 if (linep == NULL) 7432 { 7433 EMSG2(_(e_invarg2), key_start); 7434 error = TRUE; 7435 break; 7436 } 7437 } 7438 else 7439 { 7440 arg_start = linep; 7441 linep = skiptowhite(linep); 7442 } 7443 if (linep == arg_start) 7444 { 7445 EMSG2(_("E417: missing argument: %s"), key_start); 7446 error = TRUE; 7447 break; 7448 } 7449 vim_free(arg); 7450 arg = vim_strnsave(arg_start, (int)(linep - arg_start)); 7451 if (arg == NULL) 7452 { 7453 error = TRUE; 7454 break; 7455 } 7456 if (*linep == '\'') 7457 ++linep; 7458 7459 /* 7460 * Store the argument. 7461 */ 7462 if ( STRCMP(key, "TERM") == 0 7463 || STRCMP(key, "CTERM") == 0 7464 || STRCMP(key, "GUI") == 0) 7465 { 7466 attr = 0; 7467 off = 0; 7468 while (arg[off] != NUL) 7469 { 7470 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; ) 7471 { 7472 len = (int)STRLEN(hl_name_table[i]); 7473 if (STRNICMP(arg + off, hl_name_table[i], len) == 0) 7474 { 7475 attr |= hl_attr_table[i]; 7476 off += len; 7477 break; 7478 } 7479 } 7480 if (i < 0) 7481 { 7482 EMSG2(_("E418: Illegal value: %s"), arg); 7483 error = TRUE; 7484 break; 7485 } 7486 if (arg[off] == ',') /* another one follows */ 7487 ++off; 7488 } 7489 if (error) 7490 break; 7491 if (*key == 'T') 7492 { 7493 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM)) 7494 { 7495 if (!init) 7496 HL_TABLE()[idx].sg_set |= SG_TERM; 7497 HL_TABLE()[idx].sg_term = attr; 7498 } 7499 } 7500 else if (*key == 'C') 7501 { 7502 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) 7503 { 7504 if (!init) 7505 HL_TABLE()[idx].sg_set |= SG_CTERM; 7506 HL_TABLE()[idx].sg_cterm = attr; 7507 HL_TABLE()[idx].sg_cterm_bold = FALSE; 7508 } 7509 } 7510 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 7511 else 7512 { 7513 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) 7514 { 7515 if (!init) 7516 HL_TABLE()[idx].sg_set |= SG_GUI; 7517 HL_TABLE()[idx].sg_gui = attr; 7518 } 7519 } 7520 #endif 7521 } 7522 else if (STRCMP(key, "FONT") == 0) 7523 { 7524 /* in non-GUI fonts are simply ignored */ 7525 #ifdef FEAT_GUI 7526 if (!gui.shell_created) 7527 { 7528 /* GUI not started yet, always accept the name. */ 7529 vim_free(HL_TABLE()[idx].sg_font_name); 7530 HL_TABLE()[idx].sg_font_name = vim_strsave(arg); 7531 } 7532 else 7533 { 7534 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font; 7535 # ifdef FEAT_XFONTSET 7536 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset; 7537 # endif 7538 /* First, save the current font/fontset. 7539 * Then try to allocate the font/fontset. 7540 * If the allocation fails, HL_TABLE()[idx].sg_font OR 7541 * sg_fontset will be set to NOFONT or NOFONTSET respectively. 7542 */ 7543 7544 HL_TABLE()[idx].sg_font = NOFONT; 7545 # ifdef FEAT_XFONTSET 7546 HL_TABLE()[idx].sg_fontset = NOFONTSET; 7547 # endif 7548 hl_do_font(idx, arg, is_normal_group, is_menu_group, 7549 is_tooltip_group, FALSE); 7550 7551 # ifdef FEAT_XFONTSET 7552 if (HL_TABLE()[idx].sg_fontset != NOFONTSET) 7553 { 7554 /* New fontset was accepted. Free the old one, if there 7555 * was one. */ 7556 gui_mch_free_fontset(temp_sg_fontset); 7557 vim_free(HL_TABLE()[idx].sg_font_name); 7558 HL_TABLE()[idx].sg_font_name = vim_strsave(arg); 7559 } 7560 else 7561 HL_TABLE()[idx].sg_fontset = temp_sg_fontset; 7562 # endif 7563 if (HL_TABLE()[idx].sg_font != NOFONT) 7564 { 7565 /* New font was accepted. Free the old one, if there was 7566 * one. */ 7567 gui_mch_free_font(temp_sg_font); 7568 vim_free(HL_TABLE()[idx].sg_font_name); 7569 HL_TABLE()[idx].sg_font_name = vim_strsave(arg); 7570 } 7571 else 7572 HL_TABLE()[idx].sg_font = temp_sg_font; 7573 } 7574 #endif 7575 } 7576 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) 7577 { 7578 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) 7579 { 7580 if (!init) 7581 HL_TABLE()[idx].sg_set |= SG_CTERM; 7582 7583 /* When setting the foreground color, and previously the "bold" 7584 * flag was set for a light color, reset it now */ 7585 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold) 7586 { 7587 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; 7588 HL_TABLE()[idx].sg_cterm_bold = FALSE; 7589 } 7590 7591 if (VIM_ISDIGIT(*arg)) 7592 color = atoi((char *)arg); 7593 else if (STRICMP(arg, "fg") == 0) 7594 { 7595 if (cterm_normal_fg_color) 7596 color = cterm_normal_fg_color - 1; 7597 else 7598 { 7599 EMSG(_("E419: FG color unknown")); 7600 error = TRUE; 7601 break; 7602 } 7603 } 7604 else if (STRICMP(arg, "bg") == 0) 7605 { 7606 if (cterm_normal_bg_color > 0) 7607 color = cterm_normal_bg_color - 1; 7608 else 7609 { 7610 EMSG(_("E420: BG color unknown")); 7611 error = TRUE; 7612 break; 7613 } 7614 } 7615 else 7616 { 7617 static char *(color_names[28]) = { 7618 "Black", "DarkBlue", "DarkGreen", "DarkCyan", 7619 "DarkRed", "DarkMagenta", "Brown", "DarkYellow", 7620 "Gray", "Grey", 7621 "LightGray", "LightGrey", "DarkGray", "DarkGrey", 7622 "Blue", "LightBlue", "Green", "LightGreen", 7623 "Cyan", "LightCyan", "Red", "LightRed", "Magenta", 7624 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"}; 7625 static int color_numbers_16[28] = {0, 1, 2, 3, 7626 4, 5, 6, 6, 7627 7, 7, 7628 7, 7, 8, 8, 7629 9, 9, 10, 10, 7630 11, 11, 12, 12, 13, 7631 13, 14, 14, 15, -1}; 7632 /* for xterm with 88 colors... */ 7633 static int color_numbers_88[28] = {0, 4, 2, 6, 7634 1, 5, 32, 72, 7635 84, 84, 7636 7, 7, 82, 82, 7637 12, 43, 10, 61, 7638 14, 63, 9, 74, 13, 7639 75, 11, 78, 15, -1}; 7640 /* for xterm with 256 colors... */ 7641 static int color_numbers_256[28] = {0, 4, 2, 6, 7642 1, 5, 130, 130, 7643 248, 248, 7644 7, 7, 242, 242, 7645 12, 81, 10, 121, 7646 14, 159, 9, 224, 13, 7647 225, 11, 229, 15, -1}; 7648 /* for terminals with less than 16 colors... */ 7649 static int color_numbers_8[28] = {0, 4, 2, 6, 7650 1, 5, 3, 3, 7651 7, 7, 7652 7, 7, 0+8, 0+8, 7653 4+8, 4+8, 2+8, 2+8, 7654 6+8, 6+8, 1+8, 1+8, 5+8, 7655 5+8, 3+8, 3+8, 7+8, -1}; 7656 #if defined(__QNXNTO__) 7657 static int *color_numbers_8_qansi = color_numbers_8; 7658 /* On qnx, the 8 & 16 color arrays are the same */ 7659 if (STRNCMP(T_NAME, "qansi", 5) == 0) 7660 color_numbers_8_qansi = color_numbers_16; 7661 #endif 7662 7663 /* reduce calls to STRICMP a bit, it can be slow */ 7664 off = TOUPPER_ASC(*arg); 7665 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; ) 7666 if (off == color_names[i][0] 7667 && STRICMP(arg + 1, color_names[i] + 1) == 0) 7668 break; 7669 if (i < 0) 7670 { 7671 EMSG2(_("E421: Color name or number not recognized: %s"), key_start); 7672 error = TRUE; 7673 break; 7674 } 7675 7676 /* Use the _16 table to check if its a valid color name. */ 7677 color = color_numbers_16[i]; 7678 if (color >= 0) 7679 { 7680 if (t_colors == 8) 7681 { 7682 /* t_Co is 8: use the 8 colors table */ 7683 #if defined(__QNXNTO__) 7684 color = color_numbers_8_qansi[i]; 7685 #else 7686 color = color_numbers_8[i]; 7687 #endif 7688 if (key[5] == 'F') 7689 { 7690 /* set/reset bold attribute to get light foreground 7691 * colors (on some terminals, e.g. "linux") */ 7692 if (color & 8) 7693 { 7694 HL_TABLE()[idx].sg_cterm |= HL_BOLD; 7695 HL_TABLE()[idx].sg_cterm_bold = TRUE; 7696 } 7697 else 7698 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; 7699 } 7700 color &= 7; /* truncate to 8 colors */ 7701 } 7702 else if (t_colors == 16 || t_colors == 88 7703 || t_colors == 256) 7704 { 7705 /* 7706 * Guess: if the termcap entry ends in 'm', it is 7707 * probably an xterm-like terminal. Use the changed 7708 * order for colors. 7709 */ 7710 if (*T_CAF != NUL) 7711 p = T_CAF; 7712 else 7713 p = T_CSF; 7714 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm') 7715 switch (t_colors) 7716 { 7717 case 16: 7718 color = color_numbers_8[i]; 7719 break; 7720 case 88: 7721 color = color_numbers_88[i]; 7722 break; 7723 case 256: 7724 color = color_numbers_256[i]; 7725 break; 7726 } 7727 } 7728 } 7729 } 7730 /* Add one to the argument, to avoid zero. Zero is used for 7731 * "NONE", then "color" is -1. */ 7732 if (key[5] == 'F') 7733 { 7734 HL_TABLE()[idx].sg_cterm_fg = color + 1; 7735 if (is_normal_group) 7736 { 7737 cterm_normal_fg_color = color + 1; 7738 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD); 7739 #ifdef FEAT_GUI 7740 /* Don't do this if the GUI is used. */ 7741 if (!gui.in_use && !gui.starting) 7742 #endif 7743 { 7744 must_redraw = CLEAR; 7745 if (termcap_active && color >= 0) 7746 term_fg_color(color); 7747 } 7748 } 7749 } 7750 else 7751 { 7752 HL_TABLE()[idx].sg_cterm_bg = color + 1; 7753 if (is_normal_group) 7754 { 7755 cterm_normal_bg_color = color + 1; 7756 #ifdef FEAT_GUI 7757 /* Don't mess with 'background' if the GUI is used. */ 7758 if (!gui.in_use && !gui.starting) 7759 #endif 7760 { 7761 must_redraw = CLEAR; 7762 if (color >= 0) 7763 { 7764 if (termcap_active) 7765 term_bg_color(color); 7766 if (t_colors < 16) 7767 i = (color == 0 || color == 4); 7768 else 7769 i = (color < 7 || color == 8); 7770 /* Set the 'background' option if the value is 7771 * wrong. */ 7772 if (i != (*p_bg == 'd')) 7773 set_option_value((char_u *)"bg", 0L, 7774 i ? (char_u *)"dark" 7775 : (char_u *)"light", 0); 7776 } 7777 } 7778 } 7779 } 7780 } 7781 } 7782 else if (STRCMP(key, "GUIFG") == 0) 7783 { 7784 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 7785 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) 7786 { 7787 if (!init) 7788 HL_TABLE()[idx].sg_set |= SG_GUI; 7789 7790 # ifdef FEAT_GUI 7791 /* In GUI guifg colors are only used when recognized */ 7792 i = color_name2handle(arg); 7793 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use) 7794 { 7795 HL_TABLE()[idx].sg_gui_fg = i; 7796 # endif 7797 vim_free(HL_TABLE()[idx].sg_gui_fg_name); 7798 if (STRCMP(arg, "NONE")) 7799 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg); 7800 else 7801 HL_TABLE()[idx].sg_gui_fg_name = NULL; 7802 # ifdef FEAT_GUI 7803 # ifdef FEAT_GUI_X11 7804 if (is_menu_group) 7805 gui.menu_fg_pixel = i; 7806 if (is_scrollbar_group) 7807 gui.scroll_fg_pixel = i; 7808 # ifdef FEAT_BEVAL 7809 if (is_tooltip_group) 7810 gui.tooltip_fg_pixel = i; 7811 # endif 7812 do_colors = TRUE; 7813 # endif 7814 } 7815 # endif 7816 } 7817 #endif 7818 } 7819 else if (STRCMP(key, "GUIBG") == 0) 7820 { 7821 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 7822 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) 7823 { 7824 if (!init) 7825 HL_TABLE()[idx].sg_set |= SG_GUI; 7826 7827 # ifdef FEAT_GUI 7828 /* In GUI guifg colors are only used when recognized */ 7829 i = color_name2handle(arg); 7830 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use) 7831 { 7832 HL_TABLE()[idx].sg_gui_bg = i; 7833 # endif 7834 vim_free(HL_TABLE()[idx].sg_gui_bg_name); 7835 if (STRCMP(arg, "NONE") != 0) 7836 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg); 7837 else 7838 HL_TABLE()[idx].sg_gui_bg_name = NULL; 7839 # ifdef FEAT_GUI 7840 # ifdef FEAT_GUI_X11 7841 if (is_menu_group) 7842 gui.menu_bg_pixel = i; 7843 if (is_scrollbar_group) 7844 gui.scroll_bg_pixel = i; 7845 # ifdef FEAT_BEVAL 7846 if (is_tooltip_group) 7847 gui.tooltip_bg_pixel = i; 7848 # endif 7849 do_colors = TRUE; 7850 # endif 7851 } 7852 # endif 7853 } 7854 #endif 7855 } 7856 else if (STRCMP(key, "GUISP") == 0) 7857 { 7858 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 7859 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) 7860 { 7861 if (!init) 7862 HL_TABLE()[idx].sg_set |= SG_GUI; 7863 7864 # ifdef FEAT_GUI 7865 i = color_name2handle(arg); 7866 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use) 7867 { 7868 HL_TABLE()[idx].sg_gui_sp = i; 7869 # endif 7870 vim_free(HL_TABLE()[idx].sg_gui_sp_name); 7871 if (STRCMP(arg, "NONE") != 0) 7872 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg); 7873 else 7874 HL_TABLE()[idx].sg_gui_sp_name = NULL; 7875 # ifdef FEAT_GUI 7876 } 7877 # endif 7878 } 7879 #endif 7880 } 7881 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0) 7882 { 7883 char_u buf[100]; 7884 char_u *tname; 7885 7886 if (!init) 7887 HL_TABLE()[idx].sg_set |= SG_TERM; 7888 7889 /* 7890 * The "start" and "stop" arguments can be a literal escape 7891 * sequence, or a comma separated list of terminal codes. 7892 */ 7893 if (STRNCMP(arg, "t_", 2) == 0) 7894 { 7895 off = 0; 7896 buf[0] = 0; 7897 while (arg[off] != NUL) 7898 { 7899 /* Isolate one termcap name */ 7900 for (len = 0; arg[off + len] && 7901 arg[off + len] != ','; ++len) 7902 ; 7903 tname = vim_strnsave(arg + off, len); 7904 if (tname == NULL) /* out of memory */ 7905 { 7906 error = TRUE; 7907 break; 7908 } 7909 /* lookup the escape sequence for the item */ 7910 p = get_term_code(tname); 7911 vim_free(tname); 7912 if (p == NULL) /* ignore non-existing things */ 7913 p = (char_u *)""; 7914 7915 /* Append it to the already found stuff */ 7916 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99) 7917 { 7918 EMSG2(_("E422: terminal code too long: %s"), arg); 7919 error = TRUE; 7920 break; 7921 } 7922 STRCAT(buf, p); 7923 7924 /* Advance to the next item */ 7925 off += len; 7926 if (arg[off] == ',') /* another one follows */ 7927 ++off; 7928 } 7929 } 7930 else 7931 { 7932 /* 7933 * Copy characters from arg[] to buf[], translating <> codes. 7934 */ 7935 for (p = arg, off = 0; off < 100 - 6 && *p; ) 7936 { 7937 len = trans_special(&p, buf + off, FALSE); 7938 if (len > 0) /* recognized special char */ 7939 off += len; 7940 else /* copy as normal char */ 7941 buf[off++] = *p++; 7942 } 7943 buf[off] = NUL; 7944 } 7945 if (error) 7946 break; 7947 7948 if (STRCMP(buf, "NONE") == 0) /* resetting the value */ 7949 p = NULL; 7950 else 7951 p = vim_strsave(buf); 7952 if (key[2] == 'A') 7953 { 7954 vim_free(HL_TABLE()[idx].sg_start); 7955 HL_TABLE()[idx].sg_start = p; 7956 } 7957 else 7958 { 7959 vim_free(HL_TABLE()[idx].sg_stop); 7960 HL_TABLE()[idx].sg_stop = p; 7961 } 7962 } 7963 else 7964 { 7965 EMSG2(_("E423: Illegal argument: %s"), key_start); 7966 error = TRUE; 7967 break; 7968 } 7969 7970 /* 7971 * When highlighting has been given for a group, don't link it. 7972 */ 7973 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK)) 7974 HL_TABLE()[idx].sg_link = 0; 7975 7976 /* 7977 * Continue with next argument. 7978 */ 7979 linep = skipwhite(linep); 7980 } 7981 7982 /* 7983 * If there is an error, and it's a new entry, remove it from the table. 7984 */ 7985 if (error && idx == highlight_ga.ga_len) 7986 syn_unadd_group(); 7987 else 7988 { 7989 if (is_normal_group) 7990 { 7991 HL_TABLE()[idx].sg_term_attr = 0; 7992 HL_TABLE()[idx].sg_cterm_attr = 0; 7993 #ifdef FEAT_GUI 7994 HL_TABLE()[idx].sg_gui_attr = 0; 7995 /* 7996 * Need to update all groups, because they might be using "bg" 7997 * and/or "fg", which have been changed now. 7998 */ 7999 if (gui.in_use) 8000 highlight_gui_started(); 8001 #endif 8002 } 8003 #ifdef FEAT_GUI_X11 8004 # ifdef FEAT_MENU 8005 else if (is_menu_group) 8006 { 8007 if (gui.in_use && do_colors) 8008 gui_mch_new_menu_colors(); 8009 } 8010 # endif 8011 else if (is_scrollbar_group) 8012 { 8013 if (gui.in_use && do_colors) 8014 gui_new_scrollbar_colors(); 8015 } 8016 # ifdef FEAT_BEVAL 8017 else if (is_tooltip_group) 8018 { 8019 if (gui.in_use && do_colors) 8020 gui_mch_new_tooltip_colors(); 8021 } 8022 # endif 8023 #endif 8024 else 8025 set_hl_attr(idx); 8026 #ifdef FEAT_EVAL 8027 HL_TABLE()[idx].sg_scriptID = current_SID; 8028 #endif 8029 redraw_all_later(NOT_VALID); 8030 } 8031 vim_free(key); 8032 vim_free(arg); 8033 8034 /* Only call highlight_changed() once, after sourcing a syntax file */ 8035 need_highlight_changed = TRUE; 8036 } 8037 8038 #if defined(EXITFREE) || defined(PROTO) 8039 void 8040 free_highlight() 8041 { 8042 int i; 8043 8044 for (i = 0; i < highlight_ga.ga_len; ++i) 8045 { 8046 highlight_clear(i); 8047 vim_free(HL_TABLE()[i].sg_name); 8048 vim_free(HL_TABLE()[i].sg_name_u); 8049 } 8050 ga_clear(&highlight_ga); 8051 } 8052 #endif 8053 8054 /* 8055 * Reset the cterm colors to what they were before Vim was started, if 8056 * possible. Otherwise reset them to zero. 8057 */ 8058 void 8059 restore_cterm_colors() 8060 { 8061 #if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32)) 8062 /* Since t_me has been set, this probably means that the user 8063 * wants to use this as default colors. Need to reset default 8064 * background/foreground colors. */ 8065 mch_set_normal_colors(); 8066 #else 8067 cterm_normal_fg_color = 0; 8068 cterm_normal_fg_bold = 0; 8069 cterm_normal_bg_color = 0; 8070 #endif 8071 } 8072 8073 /* 8074 * Return TRUE if highlight group "idx" has any settings. 8075 * When "check_link" is TRUE also check for an existing link. 8076 */ 8077 static int 8078 hl_has_settings(idx, check_link) 8079 int idx; 8080 int check_link; 8081 { 8082 return ( HL_TABLE()[idx].sg_term_attr != 0 8083 || HL_TABLE()[idx].sg_cterm_attr != 0 8084 || HL_TABLE()[idx].sg_cterm_fg != 0 8085 || HL_TABLE()[idx].sg_cterm_bg != 0 8086 #ifdef FEAT_GUI 8087 || HL_TABLE()[idx].sg_gui_attr != 0 8088 || HL_TABLE()[idx].sg_gui_fg_name != NULL 8089 || HL_TABLE()[idx].sg_gui_bg_name != NULL 8090 || HL_TABLE()[idx].sg_gui_sp_name != NULL 8091 || HL_TABLE()[idx].sg_font_name != NUL 8092 #endif 8093 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK))); 8094 } 8095 8096 /* 8097 * Clear highlighting for one group. 8098 */ 8099 static void 8100 highlight_clear(idx) 8101 int idx; 8102 { 8103 HL_TABLE()[idx].sg_term = 0; 8104 vim_free(HL_TABLE()[idx].sg_start); 8105 HL_TABLE()[idx].sg_start = NULL; 8106 vim_free(HL_TABLE()[idx].sg_stop); 8107 HL_TABLE()[idx].sg_stop = NULL; 8108 HL_TABLE()[idx].sg_term_attr = 0; 8109 HL_TABLE()[idx].sg_cterm = 0; 8110 HL_TABLE()[idx].sg_cterm_bold = FALSE; 8111 HL_TABLE()[idx].sg_cterm_fg = 0; 8112 HL_TABLE()[idx].sg_cterm_bg = 0; 8113 HL_TABLE()[idx].sg_cterm_attr = 0; 8114 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 8115 HL_TABLE()[idx].sg_gui = 0; 8116 vim_free(HL_TABLE()[idx].sg_gui_fg_name); 8117 HL_TABLE()[idx].sg_gui_fg_name = NULL; 8118 vim_free(HL_TABLE()[idx].sg_gui_bg_name); 8119 HL_TABLE()[idx].sg_gui_bg_name = NULL; 8120 vim_free(HL_TABLE()[idx].sg_gui_sp_name); 8121 HL_TABLE()[idx].sg_gui_sp_name = NULL; 8122 #endif 8123 #ifdef FEAT_GUI 8124 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR; 8125 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR; 8126 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR; 8127 gui_mch_free_font(HL_TABLE()[idx].sg_font); 8128 HL_TABLE()[idx].sg_font = NOFONT; 8129 # ifdef FEAT_XFONTSET 8130 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset); 8131 HL_TABLE()[idx].sg_fontset = NOFONTSET; 8132 # endif 8133 vim_free(HL_TABLE()[idx].sg_font_name); 8134 HL_TABLE()[idx].sg_font_name = NULL; 8135 HL_TABLE()[idx].sg_gui_attr = 0; 8136 #endif 8137 #ifdef FEAT_EVAL 8138 /* Clear the script ID only when there is no link, since that is not 8139 * cleared. */ 8140 if (HL_TABLE()[idx].sg_link == 0) 8141 HL_TABLE()[idx].sg_scriptID = 0; 8142 #endif 8143 } 8144 8145 #if defined(FEAT_GUI) || defined(PROTO) 8146 /* 8147 * Set the normal foreground and background colors according to the "Normal" 8148 * highlighting group. For X11 also set "Menu", "Scrollbar", and 8149 * "Tooltip" colors. 8150 */ 8151 void 8152 set_normal_colors() 8153 { 8154 if (set_group_colors((char_u *)"Normal", 8155 &gui.norm_pixel, &gui.back_pixel, 8156 FALSE, TRUE, FALSE)) 8157 { 8158 gui_mch_new_colors(); 8159 must_redraw = CLEAR; 8160 } 8161 #ifdef FEAT_GUI_X11 8162 if (set_group_colors((char_u *)"Menu", 8163 &gui.menu_fg_pixel, &gui.menu_bg_pixel, 8164 TRUE, FALSE, FALSE)) 8165 { 8166 # ifdef FEAT_MENU 8167 gui_mch_new_menu_colors(); 8168 # endif 8169 must_redraw = CLEAR; 8170 } 8171 # ifdef FEAT_BEVAL 8172 if (set_group_colors((char_u *)"Tooltip", 8173 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel, 8174 FALSE, FALSE, TRUE)) 8175 { 8176 # ifdef FEAT_TOOLBAR 8177 gui_mch_new_tooltip_colors(); 8178 # endif 8179 must_redraw = CLEAR; 8180 } 8181 #endif 8182 if (set_group_colors((char_u *)"Scrollbar", 8183 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel, 8184 FALSE, FALSE, FALSE)) 8185 { 8186 gui_new_scrollbar_colors(); 8187 must_redraw = CLEAR; 8188 } 8189 #endif 8190 } 8191 8192 /* 8193 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar". 8194 */ 8195 static int 8196 set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip) 8197 char_u *name; 8198 guicolor_T *fgp; 8199 guicolor_T *bgp; 8200 int do_menu; 8201 int use_norm; 8202 int do_tooltip; 8203 { 8204 int idx; 8205 8206 idx = syn_name2id(name) - 1; 8207 if (idx >= 0) 8208 { 8209 gui_do_one_color(idx, do_menu, do_tooltip); 8210 8211 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR) 8212 *fgp = HL_TABLE()[idx].sg_gui_fg; 8213 else if (use_norm) 8214 *fgp = gui.def_norm_pixel; 8215 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR) 8216 *bgp = HL_TABLE()[idx].sg_gui_bg; 8217 else if (use_norm) 8218 *bgp = gui.def_back_pixel; 8219 return TRUE; 8220 } 8221 return FALSE; 8222 } 8223 8224 /* 8225 * Get the font of the "Normal" group. 8226 * Returns "" when it's not found or not set. 8227 */ 8228 char_u * 8229 hl_get_font_name() 8230 { 8231 int id; 8232 char_u *s; 8233 8234 id = syn_name2id((char_u *)"Normal"); 8235 if (id > 0) 8236 { 8237 s = HL_TABLE()[id - 1].sg_font_name; 8238 if (s != NULL) 8239 return s; 8240 } 8241 return (char_u *)""; 8242 } 8243 8244 /* 8245 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has 8246 * actually chosen to be used. 8247 */ 8248 void 8249 hl_set_font_name(font_name) 8250 char_u *font_name; 8251 { 8252 int id; 8253 8254 id = syn_name2id((char_u *)"Normal"); 8255 if (id > 0) 8256 { 8257 vim_free(HL_TABLE()[id - 1].sg_font_name); 8258 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name); 8259 } 8260 } 8261 8262 /* 8263 * Set background color for "Normal" group. Called by gui_set_bg_color() 8264 * when the color is known. 8265 */ 8266 void 8267 hl_set_bg_color_name(name) 8268 char_u *name; /* must have been allocated */ 8269 { 8270 int id; 8271 8272 if (name != NULL) 8273 { 8274 id = syn_name2id((char_u *)"Normal"); 8275 if (id > 0) 8276 { 8277 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name); 8278 HL_TABLE()[id - 1].sg_gui_bg_name = name; 8279 } 8280 } 8281 } 8282 8283 /* 8284 * Set foreground color for "Normal" group. Called by gui_set_fg_color() 8285 * when the color is known. 8286 */ 8287 void 8288 hl_set_fg_color_name(name) 8289 char_u *name; /* must have been allocated */ 8290 { 8291 int id; 8292 8293 if (name != NULL) 8294 { 8295 id = syn_name2id((char_u *)"Normal"); 8296 if (id > 0) 8297 { 8298 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name); 8299 HL_TABLE()[id - 1].sg_gui_fg_name = name; 8300 } 8301 } 8302 } 8303 8304 /* 8305 * Return the handle for a color name. 8306 * Returns INVALCOLOR when failed. 8307 */ 8308 static guicolor_T 8309 color_name2handle(name) 8310 char_u *name; 8311 { 8312 if (STRCMP(name, "NONE") == 0) 8313 return INVALCOLOR; 8314 8315 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0) 8316 return gui.norm_pixel; 8317 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0) 8318 return gui.back_pixel; 8319 8320 return gui_get_color(name); 8321 } 8322 8323 /* 8324 * Return the handle for a font name. 8325 * Returns NOFONT when failed. 8326 */ 8327 static GuiFont 8328 font_name2handle(name) 8329 char_u *name; 8330 { 8331 if (STRCMP(name, "NONE") == 0) 8332 return NOFONT; 8333 8334 return gui_mch_get_font(name, TRUE); 8335 } 8336 8337 # ifdef FEAT_XFONTSET 8338 /* 8339 * Return the handle for a fontset name. 8340 * Returns NOFONTSET when failed. 8341 */ 8342 static GuiFontset 8343 fontset_name2handle(name, fixed_width) 8344 char_u *name; 8345 int fixed_width; 8346 { 8347 if (STRCMP(name, "NONE") == 0) 8348 return NOFONTSET; 8349 8350 return gui_mch_get_fontset(name, TRUE, fixed_width); 8351 } 8352 # endif 8353 8354 /* 8355 * Get the font or fontset for one highlight group. 8356 */ 8357 static void 8358 hl_do_font(idx, arg, do_normal, do_menu, do_tooltip, free_font) 8359 int idx; 8360 char_u *arg; 8361 int do_normal; /* set normal font */ 8362 int do_menu UNUSED; /* set menu font */ 8363 int do_tooltip UNUSED; /* set tooltip font */ 8364 int free_font; /* free current font/fontset */ 8365 { 8366 # ifdef FEAT_XFONTSET 8367 /* If 'guifontset' is not empty, first try using the name as a 8368 * fontset. If that doesn't work, use it as a font name. */ 8369 if (*p_guifontset != NUL 8370 # ifdef FONTSET_ALWAYS 8371 || do_menu 8372 # endif 8373 # ifdef FEAT_BEVAL_TIP 8374 /* In Athena & Motif, the Tooltip highlight group is always a fontset */ 8375 || do_tooltip 8376 # endif 8377 ) 8378 { 8379 if (free_font) 8380 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset); 8381 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0 8382 # ifdef FONTSET_ALWAYS 8383 || do_menu 8384 # endif 8385 # ifdef FEAT_BEVAL_TIP 8386 || do_tooltip 8387 # endif 8388 ); 8389 } 8390 if (HL_TABLE()[idx].sg_fontset != NOFONTSET) 8391 { 8392 /* If it worked and it's the Normal group, use it as the normal 8393 * fontset. Same for the Menu group. */ 8394 if (do_normal) 8395 gui_init_font(arg, TRUE); 8396 # if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU) 8397 if (do_menu) 8398 { 8399 # ifdef FONTSET_ALWAYS 8400 gui.menu_fontset = HL_TABLE()[idx].sg_fontset; 8401 # else 8402 /* YIKES! This is a bug waiting to crash the program */ 8403 gui.menu_font = HL_TABLE()[idx].sg_fontset; 8404 # endif 8405 gui_mch_new_menu_font(); 8406 } 8407 # ifdef FEAT_BEVAL 8408 if (do_tooltip) 8409 { 8410 /* The Athena widget set cannot currently handle switching between 8411 * displaying a single font and a fontset. 8412 * If the XtNinternational resource is set to True at widget 8413 * creation, then a fontset is always used, otherwise an 8414 * XFontStruct is used. 8415 */ 8416 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset; 8417 gui_mch_new_tooltip_font(); 8418 } 8419 # endif 8420 # endif 8421 } 8422 else 8423 # endif 8424 { 8425 if (free_font) 8426 gui_mch_free_font(HL_TABLE()[idx].sg_font); 8427 HL_TABLE()[idx].sg_font = font_name2handle(arg); 8428 /* If it worked and it's the Normal group, use it as the 8429 * normal font. Same for the Menu group. */ 8430 if (HL_TABLE()[idx].sg_font != NOFONT) 8431 { 8432 if (do_normal) 8433 gui_init_font(arg, FALSE); 8434 #ifndef FONTSET_ALWAYS 8435 # if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU) 8436 if (do_menu) 8437 { 8438 gui.menu_font = HL_TABLE()[idx].sg_font; 8439 gui_mch_new_menu_font(); 8440 } 8441 # endif 8442 #endif 8443 } 8444 } 8445 } 8446 8447 #endif /* FEAT_GUI */ 8448 8449 /* 8450 * Table with the specifications for an attribute number. 8451 * Note that this table is used by ALL buffers. This is required because the 8452 * GUI can redraw at any time for any buffer. 8453 */ 8454 static garray_T term_attr_table = {0, 0, 0, 0, NULL}; 8455 8456 #define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx] 8457 8458 static garray_T cterm_attr_table = {0, 0, 0, 0, NULL}; 8459 8460 #define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx] 8461 8462 #ifdef FEAT_GUI 8463 static garray_T gui_attr_table = {0, 0, 0, 0, NULL}; 8464 8465 #define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx] 8466 #endif 8467 8468 /* 8469 * Return the attr number for a set of colors and font. 8470 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table 8471 * if the combination is new. 8472 * Return 0 for error (no more room). 8473 */ 8474 static int 8475 get_attr_entry(table, aep) 8476 garray_T *table; 8477 attrentry_T *aep; 8478 { 8479 int i; 8480 attrentry_T *taep; 8481 static int recursive = FALSE; 8482 8483 /* 8484 * Init the table, in case it wasn't done yet. 8485 */ 8486 table->ga_itemsize = sizeof(attrentry_T); 8487 table->ga_growsize = 7; 8488 8489 /* 8490 * Try to find an entry with the same specifications. 8491 */ 8492 for (i = 0; i < table->ga_len; ++i) 8493 { 8494 taep = &(((attrentry_T *)table->ga_data)[i]); 8495 if ( aep->ae_attr == taep->ae_attr 8496 && ( 8497 #ifdef FEAT_GUI 8498 (table == &gui_attr_table 8499 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color 8500 && aep->ae_u.gui.bg_color 8501 == taep->ae_u.gui.bg_color 8502 && aep->ae_u.gui.sp_color 8503 == taep->ae_u.gui.sp_color 8504 && aep->ae_u.gui.font == taep->ae_u.gui.font 8505 # ifdef FEAT_XFONTSET 8506 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset 8507 # endif 8508 )) 8509 || 8510 #endif 8511 (table == &term_attr_table 8512 && (aep->ae_u.term.start == NULL) 8513 == (taep->ae_u.term.start == NULL) 8514 && (aep->ae_u.term.start == NULL 8515 || STRCMP(aep->ae_u.term.start, 8516 taep->ae_u.term.start) == 0) 8517 && (aep->ae_u.term.stop == NULL) 8518 == (taep->ae_u.term.stop == NULL) 8519 && (aep->ae_u.term.stop == NULL 8520 || STRCMP(aep->ae_u.term.stop, 8521 taep->ae_u.term.stop) == 0)) 8522 || (table == &cterm_attr_table 8523 && aep->ae_u.cterm.fg_color 8524 == taep->ae_u.cterm.fg_color 8525 && aep->ae_u.cterm.bg_color 8526 == taep->ae_u.cterm.bg_color) 8527 )) 8528 8529 return i + ATTR_OFF; 8530 } 8531 8532 if (table->ga_len + ATTR_OFF > MAX_TYPENR) 8533 { 8534 /* 8535 * Running out of attribute entries! remove all attributes, and 8536 * compute new ones for all groups. 8537 * When called recursively, we are really out of numbers. 8538 */ 8539 if (recursive) 8540 { 8541 EMSG(_("E424: Too many different highlighting attributes in use")); 8542 return 0; 8543 } 8544 recursive = TRUE; 8545 8546 clear_hl_tables(); 8547 8548 must_redraw = CLEAR; 8549 8550 for (i = 0; i < highlight_ga.ga_len; ++i) 8551 set_hl_attr(i); 8552 8553 recursive = FALSE; 8554 } 8555 8556 /* 8557 * This is a new combination of colors and font, add an entry. 8558 */ 8559 if (ga_grow(table, 1) == FAIL) 8560 return 0; 8561 8562 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]); 8563 vim_memset(taep, 0, sizeof(attrentry_T)); 8564 taep->ae_attr = aep->ae_attr; 8565 #ifdef FEAT_GUI 8566 if (table == &gui_attr_table) 8567 { 8568 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color; 8569 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color; 8570 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color; 8571 taep->ae_u.gui.font = aep->ae_u.gui.font; 8572 # ifdef FEAT_XFONTSET 8573 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset; 8574 # endif 8575 } 8576 #endif 8577 if (table == &term_attr_table) 8578 { 8579 if (aep->ae_u.term.start == NULL) 8580 taep->ae_u.term.start = NULL; 8581 else 8582 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start); 8583 if (aep->ae_u.term.stop == NULL) 8584 taep->ae_u.term.stop = NULL; 8585 else 8586 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop); 8587 } 8588 else if (table == &cterm_attr_table) 8589 { 8590 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color; 8591 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color; 8592 } 8593 ++table->ga_len; 8594 return (table->ga_len - 1 + ATTR_OFF); 8595 } 8596 8597 /* 8598 * Clear all highlight tables. 8599 */ 8600 void 8601 clear_hl_tables() 8602 { 8603 int i; 8604 attrentry_T *taep; 8605 8606 #ifdef FEAT_GUI 8607 ga_clear(&gui_attr_table); 8608 #endif 8609 for (i = 0; i < term_attr_table.ga_len; ++i) 8610 { 8611 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]); 8612 vim_free(taep->ae_u.term.start); 8613 vim_free(taep->ae_u.term.stop); 8614 } 8615 ga_clear(&term_attr_table); 8616 ga_clear(&cterm_attr_table); 8617 } 8618 8619 #if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO) 8620 /* 8621 * Combine special attributes (e.g., for spelling) with other attributes 8622 * (e.g., for syntax highlighting). 8623 * "prim_attr" overrules "char_attr". 8624 * This creates a new group when required. 8625 * Since we expect there to be few spelling mistakes we don't cache the 8626 * result. 8627 * Return the resulting attributes. 8628 */ 8629 int 8630 hl_combine_attr(char_attr, prim_attr) 8631 int char_attr; 8632 int prim_attr; 8633 { 8634 attrentry_T *char_aep = NULL; 8635 attrentry_T *spell_aep; 8636 attrentry_T new_en; 8637 8638 if (char_attr == 0) 8639 return prim_attr; 8640 if (char_attr <= HL_ALL && prim_attr <= HL_ALL) 8641 return char_attr | prim_attr; 8642 #ifdef FEAT_GUI 8643 if (gui.in_use) 8644 { 8645 if (char_attr > HL_ALL) 8646 char_aep = syn_gui_attr2entry(char_attr); 8647 if (char_aep != NULL) 8648 new_en = *char_aep; 8649 else 8650 { 8651 vim_memset(&new_en, 0, sizeof(new_en)); 8652 new_en.ae_u.gui.fg_color = INVALCOLOR; 8653 new_en.ae_u.gui.bg_color = INVALCOLOR; 8654 new_en.ae_u.gui.sp_color = INVALCOLOR; 8655 if (char_attr <= HL_ALL) 8656 new_en.ae_attr = char_attr; 8657 } 8658 8659 if (prim_attr <= HL_ALL) 8660 new_en.ae_attr |= prim_attr; 8661 else 8662 { 8663 spell_aep = syn_gui_attr2entry(prim_attr); 8664 if (spell_aep != NULL) 8665 { 8666 new_en.ae_attr |= spell_aep->ae_attr; 8667 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR) 8668 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color; 8669 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR) 8670 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color; 8671 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR) 8672 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color; 8673 if (spell_aep->ae_u.gui.font != NOFONT) 8674 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font; 8675 # ifdef FEAT_XFONTSET 8676 if (spell_aep->ae_u.gui.fontset != NOFONTSET) 8677 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset; 8678 # endif 8679 } 8680 } 8681 return get_attr_entry(&gui_attr_table, &new_en); 8682 } 8683 #endif 8684 8685 if (t_colors > 1) 8686 { 8687 if (char_attr > HL_ALL) 8688 char_aep = syn_cterm_attr2entry(char_attr); 8689 if (char_aep != NULL) 8690 new_en = *char_aep; 8691 else 8692 { 8693 vim_memset(&new_en, 0, sizeof(new_en)); 8694 if (char_attr <= HL_ALL) 8695 new_en.ae_attr = char_attr; 8696 } 8697 8698 if (prim_attr <= HL_ALL) 8699 new_en.ae_attr |= prim_attr; 8700 else 8701 { 8702 spell_aep = syn_cterm_attr2entry(prim_attr); 8703 if (spell_aep != NULL) 8704 { 8705 new_en.ae_attr |= spell_aep->ae_attr; 8706 if (spell_aep->ae_u.cterm.fg_color > 0) 8707 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color; 8708 if (spell_aep->ae_u.cterm.bg_color > 0) 8709 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color; 8710 } 8711 } 8712 return get_attr_entry(&cterm_attr_table, &new_en); 8713 } 8714 8715 if (char_attr > HL_ALL) 8716 char_aep = syn_term_attr2entry(char_attr); 8717 if (char_aep != NULL) 8718 new_en = *char_aep; 8719 else 8720 { 8721 vim_memset(&new_en, 0, sizeof(new_en)); 8722 if (char_attr <= HL_ALL) 8723 new_en.ae_attr = char_attr; 8724 } 8725 8726 if (prim_attr <= HL_ALL) 8727 new_en.ae_attr |= prim_attr; 8728 else 8729 { 8730 spell_aep = syn_term_attr2entry(prim_attr); 8731 if (spell_aep != NULL) 8732 { 8733 new_en.ae_attr |= spell_aep->ae_attr; 8734 if (spell_aep->ae_u.term.start != NULL) 8735 { 8736 new_en.ae_u.term.start = spell_aep->ae_u.term.start; 8737 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop; 8738 } 8739 } 8740 } 8741 return get_attr_entry(&term_attr_table, &new_en); 8742 } 8743 #endif 8744 8745 #ifdef FEAT_GUI 8746 8747 attrentry_T * 8748 syn_gui_attr2entry(attr) 8749 int attr; 8750 { 8751 attr -= ATTR_OFF; 8752 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */ 8753 return NULL; 8754 return &(GUI_ATTR_ENTRY(attr)); 8755 } 8756 #endif /* FEAT_GUI */ 8757 8758 /* 8759 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr. 8760 * Only to be used when "attr" > HL_ALL. 8761 */ 8762 int 8763 syn_attr2attr(attr) 8764 int attr; 8765 { 8766 attrentry_T *aep; 8767 8768 #ifdef FEAT_GUI 8769 if (gui.in_use) 8770 aep = syn_gui_attr2entry(attr); 8771 else 8772 #endif 8773 if (t_colors > 1) 8774 aep = syn_cterm_attr2entry(attr); 8775 else 8776 aep = syn_term_attr2entry(attr); 8777 8778 if (aep == NULL) /* highlighting not set */ 8779 return 0; 8780 return aep->ae_attr; 8781 } 8782 8783 8784 attrentry_T * 8785 syn_term_attr2entry(attr) 8786 int attr; 8787 { 8788 attr -= ATTR_OFF; 8789 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */ 8790 return NULL; 8791 return &(TERM_ATTR_ENTRY(attr)); 8792 } 8793 8794 attrentry_T * 8795 syn_cterm_attr2entry(attr) 8796 int attr; 8797 { 8798 attr -= ATTR_OFF; 8799 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */ 8800 return NULL; 8801 return &(CTERM_ATTR_ENTRY(attr)); 8802 } 8803 8804 #define LIST_ATTR 1 8805 #define LIST_STRING 2 8806 #define LIST_INT 3 8807 8808 static void 8809 highlight_list_one(id) 8810 int id; 8811 { 8812 struct hl_group *sgp; 8813 int didh = FALSE; 8814 8815 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */ 8816 8817 didh = highlight_list_arg(id, didh, LIST_ATTR, 8818 sgp->sg_term, NULL, "term"); 8819 didh = highlight_list_arg(id, didh, LIST_STRING, 8820 0, sgp->sg_start, "start"); 8821 didh = highlight_list_arg(id, didh, LIST_STRING, 8822 0, sgp->sg_stop, "stop"); 8823 8824 didh = highlight_list_arg(id, didh, LIST_ATTR, 8825 sgp->sg_cterm, NULL, "cterm"); 8826 didh = highlight_list_arg(id, didh, LIST_INT, 8827 sgp->sg_cterm_fg, NULL, "ctermfg"); 8828 didh = highlight_list_arg(id, didh, LIST_INT, 8829 sgp->sg_cterm_bg, NULL, "ctermbg"); 8830 8831 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 8832 didh = highlight_list_arg(id, didh, LIST_ATTR, 8833 sgp->sg_gui, NULL, "gui"); 8834 didh = highlight_list_arg(id, didh, LIST_STRING, 8835 0, sgp->sg_gui_fg_name, "guifg"); 8836 didh = highlight_list_arg(id, didh, LIST_STRING, 8837 0, sgp->sg_gui_bg_name, "guibg"); 8838 didh = highlight_list_arg(id, didh, LIST_STRING, 8839 0, sgp->sg_gui_sp_name, "guisp"); 8840 #endif 8841 #ifdef FEAT_GUI 8842 didh = highlight_list_arg(id, didh, LIST_STRING, 8843 0, sgp->sg_font_name, "font"); 8844 #endif 8845 8846 if (sgp->sg_link && !got_int) 8847 { 8848 (void)syn_list_header(didh, 9999, id); 8849 didh = TRUE; 8850 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D)); 8851 msg_putchar(' '); 8852 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name); 8853 } 8854 8855 if (!didh) 8856 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", ""); 8857 #ifdef FEAT_EVAL 8858 if (p_verbose > 0) 8859 last_set_msg(sgp->sg_scriptID); 8860 #endif 8861 } 8862 8863 static int 8864 highlight_list_arg(id, didh, type, iarg, sarg, name) 8865 int id; 8866 int didh; 8867 int type; 8868 int iarg; 8869 char_u *sarg; 8870 char *name; 8871 { 8872 char_u buf[100]; 8873 char_u *ts; 8874 int i; 8875 8876 if (got_int) 8877 return FALSE; 8878 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0)) 8879 { 8880 ts = buf; 8881 if (type == LIST_INT) 8882 sprintf((char *)buf, "%d", iarg - 1); 8883 else if (type == LIST_STRING) 8884 ts = sarg; 8885 else /* type == LIST_ATTR */ 8886 { 8887 buf[0] = NUL; 8888 for (i = 0; hl_attr_table[i] != 0; ++i) 8889 { 8890 if (iarg & hl_attr_table[i]) 8891 { 8892 if (buf[0] != NUL) 8893 vim_strcat(buf, (char_u *)",", 100); 8894 vim_strcat(buf, (char_u *)hl_name_table[i], 100); 8895 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */ 8896 } 8897 } 8898 } 8899 8900 (void)syn_list_header(didh, 8901 (int)(vim_strsize(ts) + STRLEN(name) + 1), id); 8902 didh = TRUE; 8903 if (!got_int) 8904 { 8905 if (*name != NUL) 8906 { 8907 MSG_PUTS_ATTR(name, hl_attr(HLF_D)); 8908 MSG_PUTS_ATTR("=", hl_attr(HLF_D)); 8909 } 8910 msg_outtrans(ts); 8911 } 8912 } 8913 return didh; 8914 } 8915 8916 #if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO) 8917 /* 8918 * Return "1" if highlight group "id" has attribute "flag". 8919 * Return NULL otherwise. 8920 */ 8921 char_u * 8922 highlight_has_attr(id, flag, modec) 8923 int id; 8924 int flag; 8925 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */ 8926 { 8927 int attr; 8928 8929 if (id <= 0 || id > highlight_ga.ga_len) 8930 return NULL; 8931 8932 #if defined(FEAT_GUI) || defined(FEAT_EVAL) 8933 if (modec == 'g') 8934 attr = HL_TABLE()[id - 1].sg_gui; 8935 else 8936 #endif 8937 if (modec == 'c') 8938 attr = HL_TABLE()[id - 1].sg_cterm; 8939 else 8940 attr = HL_TABLE()[id - 1].sg_term; 8941 8942 if (attr & flag) 8943 return (char_u *)"1"; 8944 return NULL; 8945 } 8946 #endif 8947 8948 #if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO) 8949 /* 8950 * Return color name of highlight group "id". 8951 */ 8952 char_u * 8953 highlight_color(id, what, modec) 8954 int id; 8955 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */ 8956 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */ 8957 { 8958 static char_u name[20]; 8959 int n; 8960 int fg = FALSE; 8961 int sp = FALSE; 8962 int font = FALSE; 8963 8964 if (id <= 0 || id > highlight_ga.ga_len) 8965 return NULL; 8966 8967 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g') 8968 fg = TRUE; 8969 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o' 8970 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't') 8971 font = TRUE; 8972 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p') 8973 sp = TRUE; 8974 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) 8975 return NULL; 8976 if (modec == 'g') 8977 { 8978 # ifdef FEAT_GUI 8979 /* return font name */ 8980 if (font) 8981 return HL_TABLE()[id - 1].sg_font_name; 8982 8983 /* return #RRGGBB form (only possible when GUI is running) */ 8984 if (gui.in_use && what[2] == '#') 8985 { 8986 guicolor_T color; 8987 long_u rgb; 8988 static char_u buf[10]; 8989 8990 if (fg) 8991 color = HL_TABLE()[id - 1].sg_gui_fg; 8992 else if (sp) 8993 color = HL_TABLE()[id - 1].sg_gui_sp; 8994 else 8995 color = HL_TABLE()[id - 1].sg_gui_bg; 8996 if (color == INVALCOLOR) 8997 return NULL; 8998 rgb = gui_mch_get_rgb(color); 8999 sprintf((char *)buf, "#%02x%02x%02x", 9000 (unsigned)(rgb >> 16), 9001 (unsigned)(rgb >> 8) & 255, 9002 (unsigned)rgb & 255); 9003 return buf; 9004 } 9005 #endif 9006 if (fg) 9007 return (HL_TABLE()[id - 1].sg_gui_fg_name); 9008 if (sp) 9009 return (HL_TABLE()[id - 1].sg_gui_sp_name); 9010 return (HL_TABLE()[id - 1].sg_gui_bg_name); 9011 } 9012 if (font || sp) 9013 return NULL; 9014 if (modec == 'c') 9015 { 9016 if (fg) 9017 n = HL_TABLE()[id - 1].sg_cterm_fg - 1; 9018 else 9019 n = HL_TABLE()[id - 1].sg_cterm_bg - 1; 9020 sprintf((char *)name, "%d", n); 9021 return name; 9022 } 9023 /* term doesn't have color */ 9024 return NULL; 9025 } 9026 #endif 9027 9028 #if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \ 9029 || defined(PROTO) 9030 /* 9031 * Return color name of highlight group "id" as RGB value. 9032 */ 9033 long_u 9034 highlight_gui_color_rgb(id, fg) 9035 int id; 9036 int fg; /* TRUE = fg, FALSE = bg */ 9037 { 9038 guicolor_T color; 9039 9040 if (id <= 0 || id > highlight_ga.ga_len) 9041 return 0L; 9042 9043 if (fg) 9044 color = HL_TABLE()[id - 1].sg_gui_fg; 9045 else 9046 color = HL_TABLE()[id - 1].sg_gui_bg; 9047 9048 if (color == INVALCOLOR) 9049 return 0L; 9050 9051 return gui_mch_get_rgb(color); 9052 } 9053 #endif 9054 9055 /* 9056 * Output the syntax list header. 9057 * Return TRUE when started a new line. 9058 */ 9059 static int 9060 syn_list_header(did_header, outlen, id) 9061 int did_header; /* did header already */ 9062 int outlen; /* length of string that comes */ 9063 int id; /* highlight group id */ 9064 { 9065 int endcol = 19; 9066 int newline = TRUE; 9067 9068 if (!did_header) 9069 { 9070 msg_putchar('\n'); 9071 if (got_int) 9072 return TRUE; 9073 msg_outtrans(HL_TABLE()[id - 1].sg_name); 9074 endcol = 15; 9075 } 9076 else if (msg_col + outlen + 1 >= Columns) 9077 { 9078 msg_putchar('\n'); 9079 if (got_int) 9080 return TRUE; 9081 } 9082 else 9083 { 9084 if (msg_col >= endcol) /* wrap around is like starting a new line */ 9085 newline = FALSE; 9086 } 9087 9088 if (msg_col >= endcol) /* output at least one space */ 9089 endcol = msg_col + 1; 9090 if (Columns <= endcol) /* avoid hang for tiny window */ 9091 endcol = Columns - 1; 9092 9093 msg_advance(endcol); 9094 9095 /* Show "xxx" with the attributes. */ 9096 if (!did_header) 9097 { 9098 msg_puts_attr((char_u *)"xxx", syn_id2attr(id)); 9099 msg_putchar(' '); 9100 } 9101 9102 return newline; 9103 } 9104 9105 /* 9106 * Set the attribute numbers for a highlight group. 9107 * Called after one of the attributes has changed. 9108 */ 9109 static void 9110 set_hl_attr(idx) 9111 int idx; /* index in array */ 9112 { 9113 attrentry_T at_en; 9114 struct hl_group *sgp = HL_TABLE() + idx; 9115 9116 /* The "Normal" group doesn't need an attribute number */ 9117 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0) 9118 return; 9119 9120 #ifdef FEAT_GUI 9121 /* 9122 * For the GUI mode: If there are other than "normal" highlighting 9123 * attributes, need to allocate an attr number. 9124 */ 9125 if (sgp->sg_gui_fg == INVALCOLOR 9126 && sgp->sg_gui_bg == INVALCOLOR 9127 && sgp->sg_gui_sp == INVALCOLOR 9128 && sgp->sg_font == NOFONT 9129 # ifdef FEAT_XFONTSET 9130 && sgp->sg_fontset == NOFONTSET 9131 # endif 9132 ) 9133 { 9134 sgp->sg_gui_attr = sgp->sg_gui; 9135 } 9136 else 9137 { 9138 at_en.ae_attr = sgp->sg_gui; 9139 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg; 9140 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg; 9141 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp; 9142 at_en.ae_u.gui.font = sgp->sg_font; 9143 # ifdef FEAT_XFONTSET 9144 at_en.ae_u.gui.fontset = sgp->sg_fontset; 9145 # endif 9146 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en); 9147 } 9148 #endif 9149 /* 9150 * For the term mode: If there are other than "normal" highlighting 9151 * attributes, need to allocate an attr number. 9152 */ 9153 if (sgp->sg_start == NULL && sgp->sg_stop == NULL) 9154 sgp->sg_term_attr = sgp->sg_term; 9155 else 9156 { 9157 at_en.ae_attr = sgp->sg_term; 9158 at_en.ae_u.term.start = sgp->sg_start; 9159 at_en.ae_u.term.stop = sgp->sg_stop; 9160 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en); 9161 } 9162 9163 /* 9164 * For the color term mode: If there are other than "normal" 9165 * highlighting attributes, need to allocate an attr number. 9166 */ 9167 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0) 9168 sgp->sg_cterm_attr = sgp->sg_cterm; 9169 else 9170 { 9171 at_en.ae_attr = sgp->sg_cterm; 9172 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg; 9173 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg; 9174 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en); 9175 } 9176 } 9177 9178 /* 9179 * Lookup a highlight group name and return it's ID. 9180 * If it is not found, 0 is returned. 9181 */ 9182 int 9183 syn_name2id(name) 9184 char_u *name; 9185 { 9186 int i; 9187 char_u name_u[200]; 9188 9189 /* Avoid using stricmp() too much, it's slow on some systems */ 9190 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars 9191 * don't deserve to be found! */ 9192 vim_strncpy(name_u, name, 199); 9193 vim_strup(name_u); 9194 for (i = highlight_ga.ga_len; --i >= 0; ) 9195 if (HL_TABLE()[i].sg_name_u != NULL 9196 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0) 9197 break; 9198 return i + 1; 9199 } 9200 9201 #if defined(FEAT_EVAL) || defined(PROTO) 9202 /* 9203 * Return TRUE if highlight group "name" exists. 9204 */ 9205 int 9206 highlight_exists(name) 9207 char_u *name; 9208 { 9209 return (syn_name2id(name) > 0); 9210 } 9211 9212 # if defined(FEAT_SEARCH_EXTRA) || defined(PROTO) 9213 /* 9214 * Return the name of highlight group "id". 9215 * When not a valid ID return an empty string. 9216 */ 9217 char_u * 9218 syn_id2name(id) 9219 int id; 9220 { 9221 if (id <= 0 || id > highlight_ga.ga_len) 9222 return (char_u *)""; 9223 return HL_TABLE()[id - 1].sg_name; 9224 } 9225 # endif 9226 #endif 9227 9228 /* 9229 * Like syn_name2id(), but take a pointer + length argument. 9230 */ 9231 int 9232 syn_namen2id(linep, len) 9233 char_u *linep; 9234 int len; 9235 { 9236 char_u *name; 9237 int id = 0; 9238 9239 name = vim_strnsave(linep, len); 9240 if (name != NULL) 9241 { 9242 id = syn_name2id(name); 9243 vim_free(name); 9244 } 9245 return id; 9246 } 9247 9248 /* 9249 * Find highlight group name in the table and return it's ID. 9250 * The argument is a pointer to the name and the length of the name. 9251 * If it doesn't exist yet, a new entry is created. 9252 * Return 0 for failure. 9253 */ 9254 int 9255 syn_check_group(pp, len) 9256 char_u *pp; 9257 int len; 9258 { 9259 int id; 9260 char_u *name; 9261 9262 name = vim_strnsave(pp, len); 9263 if (name == NULL) 9264 return 0; 9265 9266 id = syn_name2id(name); 9267 if (id == 0) /* doesn't exist yet */ 9268 id = syn_add_group(name); 9269 else 9270 vim_free(name); 9271 return id; 9272 } 9273 9274 /* 9275 * Add new highlight group and return it's ID. 9276 * "name" must be an allocated string, it will be consumed. 9277 * Return 0 for failure. 9278 */ 9279 static int 9280 syn_add_group(name) 9281 char_u *name; 9282 { 9283 char_u *p; 9284 9285 /* Check that the name is ASCII letters, digits and underscore. */ 9286 for (p = name; *p != NUL; ++p) 9287 { 9288 if (!vim_isprintc(*p)) 9289 { 9290 EMSG(_("E669: Unprintable character in group name")); 9291 vim_free(name); 9292 return 0; 9293 } 9294 else if (!ASCII_ISALNUM(*p) && *p != '_') 9295 { 9296 /* This is an error, but since there previously was no check only 9297 * give a warning. */ 9298 msg_source(hl_attr(HLF_W)); 9299 MSG(_("W18: Invalid character in group name")); 9300 break; 9301 } 9302 } 9303 9304 /* 9305 * First call for this growarray: init growing array. 9306 */ 9307 if (highlight_ga.ga_data == NULL) 9308 { 9309 highlight_ga.ga_itemsize = sizeof(struct hl_group); 9310 highlight_ga.ga_growsize = 10; 9311 } 9312 9313 if (highlight_ga.ga_len >= MAX_HL_ID) 9314 { 9315 EMSG(_("E849: Too many highlight and syntax groups")); 9316 vim_free(name); 9317 return 0; 9318 } 9319 9320 /* 9321 * Make room for at least one other syntax_highlight entry. 9322 */ 9323 if (ga_grow(&highlight_ga, 1) == FAIL) 9324 { 9325 vim_free(name); 9326 return 0; 9327 } 9328 9329 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group)); 9330 HL_TABLE()[highlight_ga.ga_len].sg_name = name; 9331 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name); 9332 #ifdef FEAT_GUI 9333 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR; 9334 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR; 9335 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR; 9336 #endif 9337 ++highlight_ga.ga_len; 9338 9339 return highlight_ga.ga_len; /* ID is index plus one */ 9340 } 9341 9342 /* 9343 * When, just after calling syn_add_group(), an error is discovered, this 9344 * function deletes the new name. 9345 */ 9346 static void 9347 syn_unadd_group() 9348 { 9349 --highlight_ga.ga_len; 9350 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name); 9351 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u); 9352 } 9353 9354 /* 9355 * Translate a group ID to highlight attributes. 9356 */ 9357 int 9358 syn_id2attr(hl_id) 9359 int hl_id; 9360 { 9361 int attr; 9362 struct hl_group *sgp; 9363 9364 hl_id = syn_get_final_id(hl_id); 9365 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */ 9366 9367 #ifdef FEAT_GUI 9368 /* 9369 * Only use GUI attr when the GUI is being used. 9370 */ 9371 if (gui.in_use) 9372 attr = sgp->sg_gui_attr; 9373 else 9374 #endif 9375 if (t_colors > 1) 9376 attr = sgp->sg_cterm_attr; 9377 else 9378 attr = sgp->sg_term_attr; 9379 9380 return attr; 9381 } 9382 9383 #ifdef FEAT_GUI 9384 /* 9385 * Get the GUI colors and attributes for a group ID. 9386 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise. 9387 */ 9388 int 9389 syn_id2colors(hl_id, fgp, bgp) 9390 int hl_id; 9391 guicolor_T *fgp; 9392 guicolor_T *bgp; 9393 { 9394 struct hl_group *sgp; 9395 9396 hl_id = syn_get_final_id(hl_id); 9397 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */ 9398 9399 *fgp = sgp->sg_gui_fg; 9400 *bgp = sgp->sg_gui_bg; 9401 return sgp->sg_gui; 9402 } 9403 #endif 9404 9405 /* 9406 * Translate a group ID to the final group ID (following links). 9407 */ 9408 int 9409 syn_get_final_id(hl_id) 9410 int hl_id; 9411 { 9412 int count; 9413 struct hl_group *sgp; 9414 9415 if (hl_id > highlight_ga.ga_len || hl_id < 1) 9416 return 0; /* Can be called from eval!! */ 9417 9418 /* 9419 * Follow links until there is no more. 9420 * Look out for loops! Break after 100 links. 9421 */ 9422 for (count = 100; --count >= 0; ) 9423 { 9424 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */ 9425 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len) 9426 break; 9427 hl_id = sgp->sg_link; 9428 } 9429 9430 return hl_id; 9431 } 9432 9433 #ifdef FEAT_GUI 9434 /* 9435 * Call this function just after the GUI has started. 9436 * It finds the font and color handles for the highlighting groups. 9437 */ 9438 void 9439 highlight_gui_started() 9440 { 9441 int idx; 9442 9443 /* First get the colors from the "Normal" and "Menu" group, if set */ 9444 set_normal_colors(); 9445 9446 for (idx = 0; idx < highlight_ga.ga_len; ++idx) 9447 gui_do_one_color(idx, FALSE, FALSE); 9448 9449 highlight_changed(); 9450 } 9451 9452 static void 9453 gui_do_one_color(idx, do_menu, do_tooltip) 9454 int idx; 9455 int do_menu; /* TRUE: might set the menu font */ 9456 int do_tooltip; /* TRUE: might set the tooltip font */ 9457 { 9458 int didit = FALSE; 9459 9460 if (HL_TABLE()[idx].sg_font_name != NULL) 9461 { 9462 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu, 9463 do_tooltip, TRUE); 9464 didit = TRUE; 9465 } 9466 if (HL_TABLE()[idx].sg_gui_fg_name != NULL) 9467 { 9468 HL_TABLE()[idx].sg_gui_fg = 9469 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name); 9470 didit = TRUE; 9471 } 9472 if (HL_TABLE()[idx].sg_gui_bg_name != NULL) 9473 { 9474 HL_TABLE()[idx].sg_gui_bg = 9475 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name); 9476 didit = TRUE; 9477 } 9478 if (HL_TABLE()[idx].sg_gui_sp_name != NULL) 9479 { 9480 HL_TABLE()[idx].sg_gui_sp = 9481 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name); 9482 didit = TRUE; 9483 } 9484 if (didit) /* need to get a new attr number */ 9485 set_hl_attr(idx); 9486 } 9487 9488 #endif 9489 9490 /* 9491 * Translate the 'highlight' option into attributes in highlight_attr[] and 9492 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of 9493 * corresponding highlights to use on top of HLF_SNC is computed. 9494 * Called only when the 'highlight' option has been changed and upon first 9495 * screen redraw after any :highlight command. 9496 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise. 9497 */ 9498 int 9499 highlight_changed() 9500 { 9501 int hlf; 9502 int i; 9503 char_u *p; 9504 int attr; 9505 char_u *end; 9506 int id; 9507 #ifdef USER_HIGHLIGHT 9508 char_u userhl[10]; 9509 # ifdef FEAT_STL_OPT 9510 int id_SNC = -1; 9511 int id_S = -1; 9512 int hlcnt; 9513 # endif 9514 #endif 9515 static int hl_flags[HLF_COUNT] = HL_FLAGS; 9516 9517 need_highlight_changed = FALSE; 9518 9519 /* 9520 * Clear all attributes. 9521 */ 9522 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf) 9523 highlight_attr[hlf] = 0; 9524 9525 /* 9526 * First set all attributes to their default value. 9527 * Then use the attributes from the 'highlight' option. 9528 */ 9529 for (i = 0; i < 2; ++i) 9530 { 9531 if (i) 9532 p = p_hl; 9533 else 9534 p = get_highlight_default(); 9535 if (p == NULL) /* just in case */ 9536 continue; 9537 9538 while (*p) 9539 { 9540 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf) 9541 if (hl_flags[hlf] == *p) 9542 break; 9543 ++p; 9544 if (hlf == (int)HLF_COUNT || *p == NUL) 9545 return FAIL; 9546 9547 /* 9548 * Allow several hl_flags to be combined, like "bu" for 9549 * bold-underlined. 9550 */ 9551 attr = 0; 9552 for ( ; *p && *p != ','; ++p) /* parse upto comma */ 9553 { 9554 if (vim_iswhite(*p)) /* ignore white space */ 9555 continue; 9556 9557 if (attr > HL_ALL) /* Combination with ':' is not allowed. */ 9558 return FAIL; 9559 9560 switch (*p) 9561 { 9562 case 'b': attr |= HL_BOLD; 9563 break; 9564 case 'i': attr |= HL_ITALIC; 9565 break; 9566 case '-': 9567 case 'n': /* no highlighting */ 9568 break; 9569 case 'r': attr |= HL_INVERSE; 9570 break; 9571 case 's': attr |= HL_STANDOUT; 9572 break; 9573 case 'u': attr |= HL_UNDERLINE; 9574 break; 9575 case 'c': attr |= HL_UNDERCURL; 9576 break; 9577 case ':': ++p; /* highlight group name */ 9578 if (attr || *p == NUL) /* no combinations */ 9579 return FAIL; 9580 end = vim_strchr(p, ','); 9581 if (end == NULL) 9582 end = p + STRLEN(p); 9583 id = syn_check_group(p, (int)(end - p)); 9584 if (id == 0) 9585 return FAIL; 9586 attr = syn_id2attr(id); 9587 p = end - 1; 9588 #if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT) 9589 if (hlf == (int)HLF_SNC) 9590 id_SNC = syn_get_final_id(id); 9591 else if (hlf == (int)HLF_S) 9592 id_S = syn_get_final_id(id); 9593 #endif 9594 break; 9595 default: return FAIL; 9596 } 9597 } 9598 highlight_attr[hlf] = attr; 9599 9600 p = skip_to_option_part(p); /* skip comma and spaces */ 9601 } 9602 } 9603 9604 #ifdef USER_HIGHLIGHT 9605 /* Setup the user highlights 9606 * 9607 * Temporarily utilize 10 more hl entries. Have to be in there 9608 * simultaneously in case of table overflows in get_attr_entry() 9609 */ 9610 # ifdef FEAT_STL_OPT 9611 if (ga_grow(&highlight_ga, 10) == FAIL) 9612 return FAIL; 9613 hlcnt = highlight_ga.ga_len; 9614 if (id_S == 0) 9615 { /* Make sure id_S is always valid to simplify code below */ 9616 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group)); 9617 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S]; 9618 id_S = hlcnt + 10; 9619 } 9620 # endif 9621 for (i = 0; i < 9; i++) 9622 { 9623 sprintf((char *)userhl, "User%d", i + 1); 9624 id = syn_name2id(userhl); 9625 if (id == 0) 9626 { 9627 highlight_user[i] = 0; 9628 # ifdef FEAT_STL_OPT 9629 highlight_stlnc[i] = 0; 9630 # endif 9631 } 9632 else 9633 { 9634 # ifdef FEAT_STL_OPT 9635 struct hl_group *hlt = HL_TABLE(); 9636 # endif 9637 9638 highlight_user[i] = syn_id2attr(id); 9639 # ifdef FEAT_STL_OPT 9640 if (id_SNC == 0) 9641 { 9642 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group)); 9643 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC]; 9644 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC]; 9645 # if defined(FEAT_GUI) || defined(FEAT_EVAL) 9646 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC]; 9647 # endif 9648 } 9649 else 9650 mch_memmove(&hlt[hlcnt + i], 9651 &hlt[id_SNC - 1], 9652 sizeof(struct hl_group)); 9653 hlt[hlcnt + i].sg_link = 0; 9654 9655 /* Apply difference between UserX and HLF_S to HLF_SNC */ 9656 hlt[hlcnt + i].sg_term ^= 9657 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term; 9658 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start) 9659 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start; 9660 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop) 9661 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop; 9662 hlt[hlcnt + i].sg_cterm ^= 9663 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm; 9664 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg) 9665 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg; 9666 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg) 9667 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg; 9668 # if defined(FEAT_GUI) || defined(FEAT_EVAL) 9669 hlt[hlcnt + i].sg_gui ^= 9670 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui; 9671 # endif 9672 # ifdef FEAT_GUI 9673 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg) 9674 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg; 9675 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg) 9676 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg; 9677 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp) 9678 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp; 9679 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font) 9680 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font; 9681 # ifdef FEAT_XFONTSET 9682 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset) 9683 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset; 9684 # endif 9685 # endif 9686 highlight_ga.ga_len = hlcnt + i + 1; 9687 set_hl_attr(hlcnt + i); /* At long last we can apply */ 9688 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1); 9689 # endif 9690 } 9691 } 9692 # ifdef FEAT_STL_OPT 9693 highlight_ga.ga_len = hlcnt; 9694 # endif 9695 9696 #endif /* USER_HIGHLIGHT */ 9697 9698 return OK; 9699 } 9700 9701 #if defined(FEAT_CMDL_COMPL) || defined(PROTO) 9702 9703 static void highlight_list __ARGS((void)); 9704 static void highlight_list_two __ARGS((int cnt, int attr)); 9705 9706 /* 9707 * Handle command line completion for :highlight command. 9708 */ 9709 void 9710 set_context_in_highlight_cmd(xp, arg) 9711 expand_T *xp; 9712 char_u *arg; 9713 { 9714 char_u *p; 9715 9716 /* Default: expand group names */ 9717 xp->xp_context = EXPAND_HIGHLIGHT; 9718 xp->xp_pattern = arg; 9719 include_link = 2; 9720 include_default = 1; 9721 9722 /* (part of) subcommand already typed */ 9723 if (*arg != NUL) 9724 { 9725 p = skiptowhite(arg); 9726 if (*p != NUL) /* past "default" or group name */ 9727 { 9728 include_default = 0; 9729 if (STRNCMP("default", arg, p - arg) == 0) 9730 { 9731 arg = skipwhite(p); 9732 xp->xp_pattern = arg; 9733 p = skiptowhite(arg); 9734 } 9735 if (*p != NUL) /* past group name */ 9736 { 9737 include_link = 0; 9738 if (arg[1] == 'i' && arg[0] == 'N') 9739 highlight_list(); 9740 if (STRNCMP("link", arg, p - arg) == 0 9741 || STRNCMP("clear", arg, p - arg) == 0) 9742 { 9743 xp->xp_pattern = skipwhite(p); 9744 p = skiptowhite(xp->xp_pattern); 9745 if (*p != NUL) /* past first group name */ 9746 { 9747 xp->xp_pattern = skipwhite(p); 9748 p = skiptowhite(xp->xp_pattern); 9749 } 9750 } 9751 if (*p != NUL) /* past group name(s) */ 9752 xp->xp_context = EXPAND_NOTHING; 9753 } 9754 } 9755 } 9756 } 9757 9758 /* 9759 * List highlighting matches in a nice way. 9760 */ 9761 static void 9762 highlight_list() 9763 { 9764 int i; 9765 9766 for (i = 10; --i >= 0; ) 9767 highlight_list_two(i, hl_attr(HLF_D)); 9768 for (i = 40; --i >= 0; ) 9769 highlight_list_two(99, 0); 9770 } 9771 9772 static void 9773 highlight_list_two(cnt, attr) 9774 int cnt; 9775 int attr; 9776 { 9777 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr); 9778 msg_clr_eos(); 9779 out_flush(); 9780 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE); 9781 } 9782 9783 #endif /* FEAT_CMDL_COMPL */ 9784 9785 #if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \ 9786 || defined(FEAT_SIGNS) || defined(PROTO) 9787 /* 9788 * Function given to ExpandGeneric() to obtain the list of group names. 9789 * Also used for synIDattr() function. 9790 */ 9791 char_u * 9792 get_highlight_name(xp, idx) 9793 expand_T *xp UNUSED; 9794 int idx; 9795 { 9796 #ifdef FEAT_CMDL_COMPL 9797 if (idx == highlight_ga.ga_len && include_none != 0) 9798 return (char_u *)"none"; 9799 if (idx == highlight_ga.ga_len + include_none && include_default != 0) 9800 return (char_u *)"default"; 9801 if (idx == highlight_ga.ga_len + include_none + include_default 9802 && include_link != 0) 9803 return (char_u *)"link"; 9804 if (idx == highlight_ga.ga_len + include_none + include_default + 1 9805 && include_link != 0) 9806 return (char_u *)"clear"; 9807 #endif 9808 if (idx < 0 || idx >= highlight_ga.ga_len) 9809 return NULL; 9810 return HL_TABLE()[idx].sg_name; 9811 } 9812 #endif 9813 9814 #if defined(FEAT_GUI) || defined(PROTO) 9815 /* 9816 * Free all the highlight group fonts. 9817 * Used when quitting for systems which need it. 9818 */ 9819 void 9820 free_highlight_fonts() 9821 { 9822 int idx; 9823 9824 for (idx = 0; idx < highlight_ga.ga_len; ++idx) 9825 { 9826 gui_mch_free_font(HL_TABLE()[idx].sg_font); 9827 HL_TABLE()[idx].sg_font = NOFONT; 9828 # ifdef FEAT_XFONTSET 9829 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset); 9830 HL_TABLE()[idx].sg_fontset = NOFONTSET; 9831 # endif 9832 } 9833 9834 gui_mch_free_font(gui.norm_font); 9835 # ifdef FEAT_XFONTSET 9836 gui_mch_free_fontset(gui.fontset); 9837 # endif 9838 # ifndef FEAT_GUI_GTK 9839 gui_mch_free_font(gui.bold_font); 9840 gui_mch_free_font(gui.ital_font); 9841 gui_mch_free_font(gui.boldital_font); 9842 # endif 9843 } 9844 #endif 9845 9846 /************************************** 9847 * End of Highlighting stuff * 9848 **************************************/ 9849