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