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