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