Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

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