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