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