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