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