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