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