blob: c47a6026dc02e89f1f33a7eee806e9432af2f523 [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
1730 /* check for out of memory situation */
1731 if (syn_buf->b_sst_array == NULL)
1732 return 0;
1733
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001734 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001735 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001736 {
1737 clear_current_state();
1738#ifdef FEAT_EVAL
1739 current_id = 0;
1740 current_trans_id = 0;
1741#endif
1742 return 0;
1743 }
1744
Bram Moolenaar071d4272004-06-13 20:20:40 +00001745 /* Make sure current_state is valid */
1746 if (INVALID_STATE(&current_state))
1747 validate_current_state();
1748
1749 /*
1750 * Skip from the current column to "col", get the attributes for "col".
1751 */
1752 while (current_col <= col)
1753 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00001754 attr = syn_current_attr(FALSE, TRUE, can_spell);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001755 ++current_col;
1756 }
1757
Bram Moolenaar071d4272004-06-13 20:20:40 +00001758 return attr;
1759}
1760
1761/*
1762 * Get syntax attributes for current_lnum, current_col.
1763 */
1764 static int
Bram Moolenaar217ad922005-03-20 22:37:15 +00001765syn_current_attr(syncing, displaying, can_spell)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001766 int syncing; /* When 1: called for syncing */
1767 int displaying; /* result will be displayed */
Bram Moolenaar217ad922005-03-20 22:37:15 +00001768 int *can_spell; /* return: do spell checking */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001769{
1770 int syn_id;
1771 lpos_T endpos; /* was: char_u *endp; */
1772 lpos_T hl_startpos; /* was: int hl_startcol; */
1773 lpos_T hl_endpos;
1774 lpos_T eos_pos; /* end-of-start match (start region) */
1775 lpos_T eoe_pos; /* end-of-end pattern */
1776 int end_idx; /* group ID for end pattern */
1777 int idx;
1778 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001779 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001780 int startcol;
1781 int endcol;
1782 long flags;
1783 short *next_list;
1784 int found_match; /* found usable match */
1785 static int try_next_column = FALSE; /* must try in next col */
1786 int do_keywords;
1787 regmmatch_T regmatch;
1788 lpos_T pos;
1789 int lc_col;
1790 reg_extmatch_T *cur_extmatch = NULL;
1791 char_u *line; /* current line. NOTE: becomes invalid after
1792 looking for a pattern match! */
1793
1794 /* variables for zero-width matches that have a "nextgroup" argument */
1795 int keep_next_list;
1796 int zero_width_next_list = FALSE;
1797 garray_T zero_width_next_ga;
1798
1799 /*
1800 * No character, no attributes! Past end of line?
1801 * Do try matching with an empty line (could be the start of a region).
1802 */
1803 line = syn_getcurline();
1804 if (line[current_col] == NUL && current_col != 0)
1805 {
1806 /*
1807 * If we found a match after the last column, use it.
1808 */
1809 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1810 && next_match_col != MAXCOL)
1811 (void)push_next_match(NULL);
1812
1813 current_finished = TRUE;
1814 current_state_stored = FALSE;
1815 return 0;
1816 }
1817
1818 /* if the current or next character is NUL, we will finish the line now */
1819 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1820 {
1821 current_finished = TRUE;
1822 current_state_stored = FALSE;
1823 }
1824
1825 /*
1826 * When in the previous column there was a match but it could not be used
1827 * (empty match or already matched in this column) need to try again in
1828 * the next column.
1829 */
1830 if (try_next_column)
1831 {
1832 next_match_idx = -1;
1833 try_next_column = FALSE;
1834 }
1835
1836 /* Only check for keywords when not syncing and there are some. */
1837 do_keywords = !syncing
Bram Moolenaardad6b692005-01-25 22:14:34 +00001838 && (syn_buf->b_keywtab.ht_used > 0
1839 || syn_buf->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001840
1841 /* Init the list of zero-width matches with a nextlist. This is used to
1842 * avoid matching the same item in the same position twice. */
1843 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1844
1845 /*
1846 * Repeat matching keywords and patterns, to find contained items at the
1847 * same column. This stops when there are no extra matches at the current
1848 * column.
1849 */
1850 do
1851 {
1852 found_match = FALSE;
1853 keep_next_list = FALSE;
1854 syn_id = 0;
1855
1856 /*
1857 * 1. Check for a current state.
1858 * Only when there is no current state, or if the current state may
1859 * contain other things, we need to check for keywords and patterns.
1860 * Always need to check for contained items if some item has the
1861 * "containedin" argument (takes extra time!).
1862 */
1863 if (current_state.ga_len)
1864 cur_si = &CUR_STATE(current_state.ga_len - 1);
1865 else
1866 cur_si = NULL;
1867
1868 if (syn_buf->b_syn_containedin || cur_si == NULL
1869 || cur_si->si_cont_list != NULL)
1870 {
1871 /*
1872 * 2. Check for keywords, if on a keyword char after a non-keyword
1873 * char. Don't do this when syncing.
1874 */
1875 if (do_keywords)
1876 {
1877 line = syn_getcurline();
1878 if (vim_iswordc_buf(line + current_col, syn_buf)
1879 && (current_col == 0
1880 || !vim_iswordc_buf(line + current_col - 1
1881#ifdef FEAT_MBYTE
1882 - (has_mbyte
1883 ? (*mb_head_off)(line, line + current_col - 1)
1884 : 0)
1885#endif
1886 , syn_buf)))
1887 {
1888 syn_id = check_keyword_id(line, (int)current_col,
1889 &endcol, &flags, &next_list, cur_si);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001890 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001891 {
1892 if (push_current_state(KEYWORD_IDX) == OK)
1893 {
1894 cur_si = &CUR_STATE(current_state.ga_len - 1);
1895 cur_si->si_m_startcol = current_col;
1896 cur_si->si_h_startpos.lnum = current_lnum;
1897 cur_si->si_h_startpos.col = 0; /* starts right away */
1898 cur_si->si_m_endpos.lnum = current_lnum;
1899 cur_si->si_m_endpos.col = endcol;
1900 cur_si->si_h_endpos.lnum = current_lnum;
1901 cur_si->si_h_endpos.col = endcol;
1902 cur_si->si_ends = TRUE;
1903 cur_si->si_end_idx = 0;
1904 cur_si->si_flags = flags;
1905 cur_si->si_id = syn_id;
1906 cur_si->si_trans_id = syn_id;
1907 if (flags & HL_TRANSP)
1908 {
1909 if (current_state.ga_len < 2)
1910 {
1911 cur_si->si_attr = 0;
1912 cur_si->si_trans_id = 0;
1913 }
1914 else
1915 {
1916 cur_si->si_attr = CUR_STATE(
1917 current_state.ga_len - 2).si_attr;
1918 cur_si->si_trans_id = CUR_STATE(
1919 current_state.ga_len - 2).si_trans_id;
1920 }
1921 }
1922 else
1923 cur_si->si_attr = syn_id2attr(syn_id);
1924 cur_si->si_cont_list = NULL;
1925 cur_si->si_next_list = next_list;
1926 check_keepend();
1927 }
1928 else
1929 vim_free(next_list);
1930 }
1931 }
1932 }
1933
1934 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001935 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001936 */
1937 if (syn_id == 0 && syn_buf->b_syn_patterns.ga_len)
1938 {
1939 /*
1940 * If we didn't check for a match yet, or we are past it, check
1941 * for any match with a pattern.
1942 */
1943 if (next_match_idx < 0 || next_match_col < (int)current_col)
1944 {
1945 /*
1946 * Check all relevant patterns for a match at this
1947 * position. This is complicated, because matching with a
1948 * pattern takes quite a bit of time, thus we want to
1949 * avoid doing it when it's not needed.
1950 */
1951 next_match_idx = 0; /* no match in this line yet */
1952 next_match_col = MAXCOL;
1953 for (idx = syn_buf->b_syn_patterns.ga_len; --idx >= 0; )
1954 {
1955 spp = &(SYN_ITEMS(syn_buf)[idx]);
1956 if ( spp->sp_syncing == syncing
1957 && (displaying || !(spp->sp_flags & HL_DISPLAY))
1958 && (spp->sp_type == SPTYPE_MATCH
1959 || spp->sp_type == SPTYPE_START)
1960 && (current_next_list != NULL
1961 ? in_id_list(NULL, current_next_list,
1962 &spp->sp_syn, 0)
1963 : (cur_si == NULL
1964 ? !(spp->sp_flags & HL_CONTAINED)
1965 : in_id_list(cur_si,
1966 cur_si->si_cont_list, &spp->sp_syn,
1967 spp->sp_flags & HL_CONTAINED))))
1968 {
1969 /* If we already tried matching in this line, and
1970 * there isn't a match before next_match_col, skip
1971 * this item. */
1972 if (spp->sp_line_id == current_line_id
1973 && spp->sp_startcol >= next_match_col)
1974 continue;
1975 spp->sp_line_id = current_line_id;
1976
1977 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
1978 if (lc_col < 0)
1979 lc_col = 0;
1980
1981 regmatch.rmm_ic = spp->sp_ic;
1982 regmatch.regprog = spp->sp_prog;
1983 if (!syn_regexec(&regmatch, current_lnum,
1984 (colnr_T)lc_col))
1985 {
1986 /* no match in this line, try another one */
1987 spp->sp_startcol = MAXCOL;
1988 continue;
1989 }
1990
1991 /*
1992 * Compute the first column of the match.
1993 */
1994 syn_add_start_off(&pos, &regmatch,
1995 spp, SPO_MS_OFF, -1);
1996 if (pos.lnum > current_lnum)
1997 {
1998 /* must have used end of match in a next line,
1999 * we can't handle that */
2000 spp->sp_startcol = MAXCOL;
2001 continue;
2002 }
2003 startcol = pos.col;
2004
2005 /* remember the next column where this pattern
2006 * matches in the current line */
2007 spp->sp_startcol = startcol;
2008
2009 /*
2010 * If a previously found match starts at a lower
2011 * column number, don't use this one.
2012 */
2013 if (startcol >= next_match_col)
2014 continue;
2015
2016 /*
2017 * If we matched this pattern at this position
2018 * before, skip it. Must retry in the next
2019 * column, because it may match from there.
2020 */
2021 if (did_match_already(idx, &zero_width_next_ga))
2022 {
2023 try_next_column = TRUE;
2024 continue;
2025 }
2026
2027 endpos.lnum = regmatch.endpos[0].lnum;
2028 endpos.col = regmatch.endpos[0].col;
2029
2030 /* Compute the highlight start. */
2031 syn_add_start_off(&hl_startpos, &regmatch,
2032 spp, SPO_HS_OFF, -1);
2033
2034 /* Compute the region start. */
2035 /* Default is to use the end of the match. */
2036 syn_add_end_off(&eos_pos, &regmatch,
2037 spp, SPO_RS_OFF, 0);
2038
2039 /*
2040 * Grab the external submatches before they get
2041 * overwritten. Reference count doesn't change.
2042 */
2043 unref_extmatch(cur_extmatch);
2044 cur_extmatch = re_extmatch_out;
2045 re_extmatch_out = NULL;
2046
2047 flags = 0;
2048 eoe_pos.lnum = 0; /* avoid warning */
2049 eoe_pos.col = 0;
2050 end_idx = 0;
2051 hl_endpos.lnum = 0;
2052
2053 /*
2054 * For a "oneline" the end must be found in the
2055 * same line too. Search for it after the end of
2056 * the match with the start pattern. Set the
2057 * resulting end positions at the same time.
2058 */
2059 if (spp->sp_type == SPTYPE_START
2060 && (spp->sp_flags & HL_ONELINE))
2061 {
2062 lpos_T startpos;
2063
2064 startpos = endpos;
2065 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2066 &flags, &eoe_pos, &end_idx, cur_extmatch);
2067 if (endpos.lnum == 0)
2068 continue; /* not found */
2069 }
2070
2071 /*
2072 * For a "match" the size must be > 0 after the
2073 * end offset needs has been added. Except when
2074 * syncing.
2075 */
2076 else if (spp->sp_type == SPTYPE_MATCH)
2077 {
2078 syn_add_end_off(&hl_endpos, &regmatch, spp,
2079 SPO_HE_OFF, 0);
2080 syn_add_end_off(&endpos, &regmatch, spp,
2081 SPO_ME_OFF, 0);
2082 if (endpos.lnum == current_lnum
2083 && (int)endpos.col + syncing < startcol)
2084 {
2085 /*
2086 * If an empty string is matched, may need
2087 * to try matching again at next column.
2088 */
2089 if (regmatch.startpos[0].col
2090 == regmatch.endpos[0].col)
2091 try_next_column = TRUE;
2092 continue;
2093 }
2094 }
2095
2096 /*
2097 * keep the best match so far in next_match_*
2098 */
2099 /* Highlighting must start after startpos and end
2100 * before endpos. */
2101 if (hl_startpos.lnum == current_lnum
2102 && (int)hl_startpos.col < startcol)
2103 hl_startpos.col = startcol;
2104 limit_pos_zero(&hl_endpos, &endpos);
2105
2106 next_match_idx = idx;
2107 next_match_col = startcol;
2108 next_match_m_endpos = endpos;
2109 next_match_h_endpos = hl_endpos;
2110 next_match_h_startpos = hl_startpos;
2111 next_match_flags = flags;
2112 next_match_eos_pos = eos_pos;
2113 next_match_eoe_pos = eoe_pos;
2114 next_match_end_idx = end_idx;
2115 unref_extmatch(next_match_extmatch);
2116 next_match_extmatch = cur_extmatch;
2117 cur_extmatch = NULL;
2118 }
2119 }
2120 }
2121
2122 /*
2123 * If we found a match at the current column, use it.
2124 */
2125 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2126 {
2127 synpat_T *lspp;
2128
2129 /* When a zero-width item matched which has a nextgroup,
2130 * don't push the item but set nextgroup. */
2131 lspp = &(SYN_ITEMS(syn_buf)[next_match_idx]);
2132 if (next_match_m_endpos.lnum == current_lnum
2133 && next_match_m_endpos.col == current_col
2134 && lspp->sp_next_list != NULL)
2135 {
2136 current_next_list = lspp->sp_next_list;
2137 current_next_flags = lspp->sp_flags;
2138 keep_next_list = TRUE;
2139 zero_width_next_list = TRUE;
2140
2141 /* Add the index to a list, so that we can check
2142 * later that we don't match it again (and cause an
2143 * endless loop). */
2144 if (ga_grow(&zero_width_next_ga, 1) == OK)
2145 {
2146 ((int *)(zero_width_next_ga.ga_data))
2147 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002148 }
2149 next_match_idx = -1;
2150 }
2151 else
2152 cur_si = push_next_match(cur_si);
2153 found_match = TRUE;
2154 }
2155 }
2156 }
2157
2158 /*
2159 * Handle searching for nextgroup match.
2160 */
2161 if (current_next_list != NULL && !keep_next_list)
2162 {
2163 /*
2164 * If a nextgroup was not found, continue looking for one if:
2165 * - this is an empty line and the "skipempty" option was given
2166 * - we are on white space and the "skipwhite" option was given
2167 */
2168 if (!found_match)
2169 {
2170 line = syn_getcurline();
2171 if (((current_next_flags & HL_SKIPWHITE)
2172 && vim_iswhite(line[current_col]))
2173 || ((current_next_flags & HL_SKIPEMPTY)
2174 && *line == NUL))
2175 break;
2176 }
2177
2178 /*
2179 * If a nextgroup was found: Use it, and continue looking for
2180 * contained matches.
2181 * If a nextgroup was not found: Continue looking for a normal
2182 * match.
2183 * When did set current_next_list for a zero-width item and no
2184 * match was found don't loop (would get stuck).
2185 */
2186 current_next_list = NULL;
2187 next_match_idx = -1;
2188 if (!zero_width_next_list)
2189 found_match = TRUE;
2190 }
2191
2192 } while (found_match);
2193
2194 /*
2195 * Use attributes from the current state, if within its highlighting.
2196 * If not, use attributes from the current-but-one state, etc.
2197 */
2198 current_attr = 0;
2199#ifdef FEAT_EVAL
2200 current_id = 0;
2201 current_trans_id = 0;
2202#endif
2203 if (cur_si != NULL)
2204 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002205#ifndef FEAT_EVAL
2206 int current_trans_id = 0;
2207#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002208 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2209 {
2210 sip = &CUR_STATE(idx);
2211 if ((current_lnum > sip->si_h_startpos.lnum
2212 || (current_lnum == sip->si_h_startpos.lnum
2213 && current_col >= sip->si_h_startpos.col))
2214 && (sip->si_h_endpos.lnum == 0
2215 || current_lnum < sip->si_h_endpos.lnum
2216 || (current_lnum == sip->si_h_endpos.lnum
2217 && current_col < sip->si_h_endpos.col)))
2218 {
2219 current_attr = sip->si_attr;
2220#ifdef FEAT_EVAL
2221 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002222#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002223 current_trans_id = sip->si_trans_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002224 break;
2225 }
2226 }
2227
Bram Moolenaar217ad922005-03-20 22:37:15 +00002228 if (can_spell != NULL)
2229 {
2230 struct sp_syn sps;
2231
2232 /*
2233 * set "can_spell" to TRUE if spell checking is supposed to be
2234 * done in the current item.
2235 */
Bram Moolenaar217ad922005-03-20 22:37:15 +00002236 if (syn_buf->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002237 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002238 /* There is no @Spell cluster: Do spelling for items without
2239 * @NoSpell cluster. */
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002240 if (syn_buf->b_nospell_cluster_id == 0 || current_trans_id == 0)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002241 *can_spell = (syn_buf->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002242 else
2243 {
2244 sps.inc_tag = 0;
2245 sps.id = syn_buf->b_nospell_cluster_id;
2246 sps.cont_in_list = NULL;
2247 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2248 }
2249 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002250 else
2251 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002252 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002253 * the @Spell cluster. But not when @NoSpell is also there.
2254 * At the toplevel only spell check when ":syn spell toplevel"
2255 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002256 if (current_trans_id == 0)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002257 *can_spell = (syn_buf->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002258 else
2259 {
2260 sps.inc_tag = 0;
2261 sps.id = syn_buf->b_spell_cluster_id;
2262 sps.cont_in_list = NULL;
2263 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2264
2265 if (syn_buf->b_nospell_cluster_id != 0)
2266 {
2267 sps.id = syn_buf->b_nospell_cluster_id;
2268 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2269 *can_spell = FALSE;
2270 }
2271 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002272 }
2273 }
2274
2275
Bram Moolenaar071d4272004-06-13 20:20:40 +00002276 /*
2277 * Check for end of current state (and the states before it) at the
2278 * next column. Don't do this for syncing, because we would miss a
2279 * single character match.
2280 * First check if the current state ends at the current column. It
2281 * may be for an empty match and a containing item might end in the
2282 * current column.
2283 */
2284 if (!syncing)
2285 {
2286 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002287 if (current_state.ga_len > 0
2288 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002289 {
2290 ++current_col;
2291 check_state_ends();
2292 --current_col;
2293 }
2294 }
2295 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002296 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002297 /* Default: Only do spelling when there is no @Spell cluster or when
2298 * ":syn spell toplevel" was used. */
2299 *can_spell = syn_buf->b_syn_spell == SYNSPL_DEFAULT
2300 ? (syn_buf->b_spell_cluster_id == 0)
2301 : (syn_buf->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002302
2303 /* nextgroup ends at end of line, unless "skipnl" or "skipemtpy" present */
2304 if (current_next_list != NULL
2305 && syn_getcurline()[current_col + 1] == NUL
2306 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2307 current_next_list = NULL;
2308
2309 if (zero_width_next_ga.ga_len > 0)
2310 ga_clear(&zero_width_next_ga);
2311
2312 /* No longer need external matches. But keep next_match_extmatch. */
2313 unref_extmatch(re_extmatch_out);
2314 re_extmatch_out = NULL;
2315 unref_extmatch(cur_extmatch);
2316
2317 return current_attr;
2318}
2319
2320
2321/*
2322 * Check if we already matched pattern "idx" at the current column.
2323 */
2324 static int
2325did_match_already(idx, gap)
2326 int idx;
2327 garray_T *gap;
2328{
2329 int i;
2330
2331 for (i = current_state.ga_len; --i >= 0; )
2332 if (CUR_STATE(i).si_m_startcol == (int)current_col
2333 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2334 && CUR_STATE(i).si_idx == idx)
2335 return TRUE;
2336
2337 /* Zero-width matches with a nextgroup argument are not put on the syntax
2338 * stack, and can only be matched once anyway. */
2339 for (i = gap->ga_len; --i >= 0; )
2340 if (((int *)(gap->ga_data))[i] == idx)
2341 return TRUE;
2342
2343 return FALSE;
2344}
2345
2346/*
2347 * Push the next match onto the stack.
2348 */
2349 static stateitem_T *
2350push_next_match(cur_si)
2351 stateitem_T *cur_si;
2352{
2353 synpat_T *spp;
2354
2355 spp = &(SYN_ITEMS(syn_buf)[next_match_idx]);
2356
2357 /*
2358 * Push the item in current_state stack;
2359 */
2360 if (push_current_state(next_match_idx) == OK)
2361 {
2362 /*
2363 * If it's a start-skip-end type that crosses lines, figure out how
2364 * much it continues in this line. Otherwise just fill in the length.
2365 */
2366 cur_si = &CUR_STATE(current_state.ga_len - 1);
2367 cur_si->si_h_startpos = next_match_h_startpos;
2368 cur_si->si_m_startcol = current_col;
2369 cur_si->si_m_lnum = current_lnum;
2370 cur_si->si_flags = spp->sp_flags;
2371 cur_si->si_next_list = spp->sp_next_list;
2372 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2373 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2374 {
2375 /* Try to find the end pattern in the current line */
2376 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2377 check_keepend();
2378 }
2379 else
2380 {
2381 cur_si->si_m_endpos = next_match_m_endpos;
2382 cur_si->si_h_endpos = next_match_h_endpos;
2383 cur_si->si_ends = TRUE;
2384 cur_si->si_flags |= next_match_flags;
2385 cur_si->si_eoe_pos = next_match_eoe_pos;
2386 cur_si->si_end_idx = next_match_end_idx;
2387 }
2388 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2389 keepend_level = current_state.ga_len - 1;
2390 check_keepend();
2391 update_si_attr(current_state.ga_len - 1);
2392
2393 /*
2394 * If the start pattern has another highlight group, push another item
2395 * on the stack for the start pattern.
2396 */
2397 if ( spp->sp_type == SPTYPE_START
2398 && spp->sp_syn_match_id != 0
2399 && push_current_state(next_match_idx) == OK)
2400 {
2401 cur_si = &CUR_STATE(current_state.ga_len - 1);
2402 cur_si->si_h_startpos = next_match_h_startpos;
2403 cur_si->si_m_startcol = current_col;
2404 cur_si->si_m_lnum = current_lnum;
2405 cur_si->si_m_endpos = next_match_eos_pos;
2406 cur_si->si_h_endpos = next_match_eos_pos;
2407 cur_si->si_ends = TRUE;
2408 cur_si->si_end_idx = 0;
2409 cur_si->si_flags = HL_MATCH;
2410 cur_si->si_next_list = NULL;
2411 check_keepend();
2412 update_si_attr(current_state.ga_len - 1);
2413 }
2414 }
2415
2416 next_match_idx = -1; /* try other match next time */
2417
2418 return cur_si;
2419}
2420
2421/*
2422 * Check for end of current state (and the states before it).
2423 */
2424 static void
2425check_state_ends()
2426{
2427 stateitem_T *cur_si;
2428 int had_extend = FALSE;
2429
2430 cur_si = &CUR_STATE(current_state.ga_len - 1);
2431 for (;;)
2432 {
2433 if (cur_si->si_ends
2434 && (cur_si->si_m_endpos.lnum < current_lnum
2435 || (cur_si->si_m_endpos.lnum == current_lnum
2436 && cur_si->si_m_endpos.col <= current_col)))
2437 {
2438 /*
2439 * If there is an end pattern group ID, highlight the end pattern
2440 * now. No need to pop the current item from the stack.
2441 * Only do this if the end pattern continues beyond the current
2442 * position.
2443 */
2444 if (cur_si->si_end_idx
2445 && (cur_si->si_eoe_pos.lnum > current_lnum
2446 || (cur_si->si_eoe_pos.lnum == current_lnum
2447 && cur_si->si_eoe_pos.col > current_col)))
2448 {
2449 cur_si->si_idx = cur_si->si_end_idx;
2450 cur_si->si_end_idx = 0;
2451 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2452 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2453 cur_si->si_flags |= HL_MATCH;
2454 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002455
2456 /* what matches next may be different now, clear it */
2457 next_match_idx = 0;
2458 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002459 break;
2460 }
2461 else
2462 {
2463 /* handle next_list, unless at end of line and no "skipnl" or
2464 * "skipempty" */
2465 current_next_list = cur_si->si_next_list;
2466 current_next_flags = cur_si->si_flags;
2467 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2468 && syn_getcurline()[current_col] == NUL)
2469 current_next_list = NULL;
2470
2471 /* When the ended item has "extend", another item with
2472 * "keepend" now needs to check for its end. */
2473 if (cur_si->si_flags & HL_EXTEND)
2474 had_extend = TRUE;
2475
2476 pop_current_state();
2477
2478 if (current_state.ga_len == 0)
2479 break;
2480
2481 if (had_extend)
2482 {
2483 syn_update_ends(FALSE);
2484 if (current_state.ga_len == 0)
2485 break;
2486 }
2487
2488 cur_si = &CUR_STATE(current_state.ga_len - 1);
2489
2490 /*
2491 * Only for a region the search for the end continues after
2492 * the end of the contained item. If the contained match
2493 * included the end-of-line, break here, the region continues.
2494 * Don't do this when:
2495 * - "keepend" is used for the contained item
2496 * - not at the end of the line (could be end="x$"me=e-1).
2497 * - "excludenl" is used (HL_HAS_EOL won't be set)
2498 */
2499 if (cur_si->si_idx >= 0
2500 && SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_type
2501 == SPTYPE_START
2502 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2503 {
2504 update_si_end(cur_si, (int)current_col, TRUE);
2505 check_keepend();
2506 if ((current_next_flags & HL_HAS_EOL)
2507 && keepend_level < 0
2508 && syn_getcurline()[current_col] == NUL)
2509 break;
2510 }
2511 }
2512 }
2513 else
2514 break;
2515 }
2516}
2517
2518/*
2519 * Update an entry in the current_state stack for a match or region. This
2520 * fills in si_attr, si_next_list and si_cont_list.
2521 */
2522 static void
2523update_si_attr(idx)
2524 int idx;
2525{
2526 stateitem_T *sip = &CUR_STATE(idx);
2527 synpat_T *spp;
2528
2529 spp = &(SYN_ITEMS(syn_buf)[sip->si_idx]);
2530 if (sip->si_flags & HL_MATCH)
2531 sip->si_id = spp->sp_syn_match_id;
2532 else
2533 sip->si_id = spp->sp_syn.id;
2534 sip->si_attr = syn_id2attr(sip->si_id);
2535 sip->si_trans_id = sip->si_id;
2536 if (sip->si_flags & HL_MATCH)
2537 sip->si_cont_list = NULL;
2538 else
2539 sip->si_cont_list = spp->sp_cont_list;
2540
2541 /*
2542 * For transparent items, take attr from outer item.
2543 * Also take cont_list, if there is none.
2544 * Don't do this for the matchgroup of a start or end pattern.
2545 */
2546 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2547 {
2548 if (idx == 0)
2549 {
2550 sip->si_attr = 0;
2551 sip->si_trans_id = 0;
2552 if (sip->si_cont_list == NULL)
2553 sip->si_cont_list = ID_LIST_ALL;
2554 }
2555 else
2556 {
2557 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2558 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002559 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2560 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002561 if (sip->si_cont_list == NULL)
2562 {
2563 sip->si_flags |= HL_TRANS_CONT;
2564 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2565 }
2566 }
2567 }
2568}
2569
2570/*
2571 * Check the current stack for patterns with "keepend" flag.
2572 * Propagate the match-end to contained items, until a "skipend" item is found.
2573 */
2574 static void
2575check_keepend()
2576{
2577 int i;
2578 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002579 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002580 stateitem_T *sip;
2581
2582 /*
2583 * This check can consume a lot of time; only do it from the level where
2584 * there really is a keepend.
2585 */
2586 if (keepend_level < 0)
2587 return;
2588
2589 /*
2590 * Find the last index of an "extend" item. "keepend" items before that
2591 * won't do anything. If there is no "extend" item "i" will be
2592 * "keepend_level" and all "keepend" items will work normally.
2593 */
2594 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2595 if (CUR_STATE(i).si_flags & HL_EXTEND)
2596 break;
2597
2598 maxpos.lnum = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002599 maxpos_h.lnum = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002600 for ( ; i < current_state.ga_len; ++i)
2601 {
2602 sip = &CUR_STATE(i);
2603 if (maxpos.lnum != 0)
2604 {
2605 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002606 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002607 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2608 sip->si_ends = TRUE;
2609 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002610 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2611 {
2612 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002613 || maxpos.lnum > sip->si_m_endpos.lnum
2614 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002615 && maxpos.col > sip->si_m_endpos.col))
2616 maxpos = sip->si_m_endpos;
2617 if (maxpos_h.lnum == 0
2618 || maxpos_h.lnum > sip->si_h_endpos.lnum
2619 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2620 && maxpos_h.col > sip->si_h_endpos.col))
2621 maxpos_h = sip->si_h_endpos;
2622 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002623 }
2624}
2625
2626/*
2627 * Update an entry in the current_state stack for a start-skip-end pattern.
2628 * This finds the end of the current item, if it's in the current line.
2629 *
2630 * Return the flags for the matched END.
2631 */
2632 static void
2633update_si_end(sip, startcol, force)
2634 stateitem_T *sip;
2635 int startcol; /* where to start searching for the end */
2636 int force; /* when TRUE overrule a previous end */
2637{
2638 lpos_T startpos;
2639 lpos_T endpos;
2640 lpos_T hl_endpos;
2641 lpos_T end_endpos;
2642 int end_idx;
2643
2644 /* Don't update when it's already done. Can be a match of an end pattern
2645 * that started in a previous line. Watch out: can also be a "keepend"
2646 * from a containing item. */
2647 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2648 return;
2649
2650 /*
2651 * We need to find the end of the region. It may continue in the next
2652 * line.
2653 */
2654 end_idx = 0;
2655 startpos.lnum = current_lnum;
2656 startpos.col = startcol;
2657 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2658 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2659
2660 if (endpos.lnum == 0)
2661 {
2662 /* No end pattern matched. */
2663 if (SYN_ITEMS(syn_buf)[sip->si_idx].sp_flags & HL_ONELINE)
2664 {
2665 /* a "oneline" never continues in the next line */
2666 sip->si_ends = TRUE;
2667 sip->si_m_endpos.lnum = current_lnum;
2668 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2669 }
2670 else
2671 {
2672 /* continues in the next line */
2673 sip->si_ends = FALSE;
2674 sip->si_m_endpos.lnum = 0;
2675 }
2676 sip->si_h_endpos = sip->si_m_endpos;
2677 }
2678 else
2679 {
2680 /* match within this line */
2681 sip->si_m_endpos = endpos;
2682 sip->si_h_endpos = hl_endpos;
2683 sip->si_eoe_pos = end_endpos;
2684 sip->si_ends = TRUE;
2685 sip->si_end_idx = end_idx;
2686 }
2687}
2688
2689/*
2690 * Add a new state to the current state stack.
2691 * It is cleared and the index set to "idx".
2692 * Return FAIL if it's not possible (out of memory).
2693 */
2694 static int
2695push_current_state(idx)
2696 int idx;
2697{
2698 if (ga_grow(&current_state, 1) == FAIL)
2699 return FAIL;
2700 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2701 CUR_STATE(current_state.ga_len).si_idx = idx;
2702 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002703 return OK;
2704}
2705
2706/*
2707 * Remove a state from the current_state stack.
2708 */
2709 static void
2710pop_current_state()
2711{
2712 if (current_state.ga_len)
2713 {
2714 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2715 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002716 }
2717 /* after the end of a pattern, try matching a keyword or pattern */
2718 next_match_idx = -1;
2719
2720 /* if first state with "keepend" is popped, reset keepend_level */
2721 if (keepend_level >= current_state.ga_len)
2722 keepend_level = -1;
2723}
2724
2725/*
2726 * Find the end of a start/skip/end syntax region after "startpos".
2727 * Only checks one line.
2728 * Also handles a match item that continued from a previous line.
2729 * If not found, the syntax item continues in the next line. m_endpos->lnum
2730 * will be 0.
2731 * If found, the end of the region and the end of the highlighting is
2732 * computed.
2733 */
2734 static void
2735find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2736 end_idx, start_ext)
2737 int idx; /* index of the pattern */
2738 lpos_T *startpos; /* where to start looking for an END match */
2739 lpos_T *m_endpos; /* return: end of match */
2740 lpos_T *hl_endpos; /* return: end of highlighting */
2741 long *flagsp; /* return: flags of matching END */
2742 lpos_T *end_endpos; /* return: end of end pattern match */
2743 int *end_idx; /* return: group ID for end pat. match, or 0 */
2744 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2745{
2746 colnr_T matchcol;
2747 synpat_T *spp, *spp_skip;
2748 int start_idx;
2749 int best_idx;
2750 regmmatch_T regmatch;
2751 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2752 lpos_T pos;
2753 char_u *line;
2754 int had_match = FALSE;
2755
2756 /*
2757 * Check for being called with a START pattern.
2758 * Can happen with a match that continues to the next line, because it
2759 * contained a region.
2760 */
2761 spp = &(SYN_ITEMS(syn_buf)[idx]);
2762 if (spp->sp_type != SPTYPE_START)
2763 {
2764 *hl_endpos = *startpos;
2765 return;
2766 }
2767
2768 /*
2769 * Find the SKIP or first END pattern after the last START pattern.
2770 */
2771 for (;;)
2772 {
2773 spp = &(SYN_ITEMS(syn_buf)[idx]);
2774 if (spp->sp_type != SPTYPE_START)
2775 break;
2776 ++idx;
2777 }
2778
2779 /*
2780 * Lookup the SKIP pattern (if present)
2781 */
2782 if (spp->sp_type == SPTYPE_SKIP)
2783 {
2784 spp_skip = spp;
2785 ++idx;
2786 }
2787 else
2788 spp_skip = NULL;
2789
2790 /* Setup external matches for syn_regexec(). */
2791 unref_extmatch(re_extmatch_in);
2792 re_extmatch_in = ref_extmatch(start_ext);
2793
2794 matchcol = startpos->col; /* start looking for a match at sstart */
2795 start_idx = idx; /* remember the first END pattern. */
2796 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2797 for (;;)
2798 {
2799 /*
2800 * Find end pattern that matches first after "matchcol".
2801 */
2802 best_idx = -1;
2803 for (idx = start_idx; idx < syn_buf->b_syn_patterns.ga_len; ++idx)
2804 {
2805 int lc_col = matchcol;
2806
2807 spp = &(SYN_ITEMS(syn_buf)[idx]);
2808 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2809 break;
2810 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2811 if (lc_col < 0)
2812 lc_col = 0;
2813
2814 regmatch.rmm_ic = spp->sp_ic;
2815 regmatch.regprog = spp->sp_prog;
2816 if (syn_regexec(&regmatch, startpos->lnum, lc_col))
2817 {
2818 if (best_idx == -1 || regmatch.startpos[0].col
2819 < best_regmatch.startpos[0].col)
2820 {
2821 best_idx = idx;
2822 best_regmatch.startpos[0] = regmatch.startpos[0];
2823 best_regmatch.endpos[0] = regmatch.endpos[0];
2824 }
2825 }
2826 }
2827
2828 /*
2829 * If all end patterns have been tried, and there is no match, the
2830 * item continues until end-of-line.
2831 */
2832 if (best_idx == -1)
2833 break;
2834
2835 /*
2836 * If the skip pattern matches before the end pattern,
2837 * continue searching after the skip pattern.
2838 */
2839 if (spp_skip != NULL)
2840 {
2841 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2842
2843 if (lc_col < 0)
2844 lc_col = 0;
2845 regmatch.rmm_ic = spp_skip->sp_ic;
2846 regmatch.regprog = spp_skip->sp_prog;
2847 if (syn_regexec(&regmatch, startpos->lnum, lc_col)
2848 && regmatch.startpos[0].col
2849 <= best_regmatch.startpos[0].col)
2850 {
2851 /* Add offset to skip pattern match */
2852 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2853
2854 /* If the skip pattern goes on to the next line, there is no
2855 * match with an end pattern in this line. */
2856 if (pos.lnum > startpos->lnum)
2857 break;
2858
2859 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
2860
2861 /* take care of an empty match or negative offset */
2862 if (pos.col <= matchcol)
2863 ++matchcol;
2864 else if (pos.col <= regmatch.endpos[0].col)
2865 matchcol = pos.col;
2866 else
2867 /* Be careful not to jump over the NUL at the end-of-line */
2868 for (matchcol = regmatch.endpos[0].col;
2869 line[matchcol] != NUL && matchcol < pos.col;
2870 ++matchcol)
2871 ;
2872
2873 /* if the skip pattern includes end-of-line, break here */
2874 if (line[matchcol] == NUL)
2875 break;
2876
2877 continue; /* start with first end pattern again */
2878 }
2879 }
2880
2881 /*
2882 * Match from start pattern to end pattern.
2883 * Correct for match and highlight offset of end pattern.
2884 */
2885 spp = &(SYN_ITEMS(syn_buf)[best_idx]);
2886 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
2887 /* can't end before the start */
2888 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
2889 m_endpos->col = startpos->col;
2890
2891 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
2892 /* can't end before the start */
2893 if (end_endpos->lnum == startpos->lnum
2894 && end_endpos->col < startpos->col)
2895 end_endpos->col = startpos->col;
2896 /* can't end after the match */
2897 limit_pos(end_endpos, m_endpos);
2898
2899 /*
2900 * If the end group is highlighted differently, adjust the pointers.
2901 */
2902 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
2903 {
2904 *end_idx = best_idx;
2905 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
2906 {
2907 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
2908 hl_endpos->col = best_regmatch.endpos[0].col;
2909 }
2910 else
2911 {
2912 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
2913 hl_endpos->col = best_regmatch.startpos[0].col;
2914 }
2915 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
2916
2917 /* can't end before the start */
2918 if (hl_endpos->lnum == startpos->lnum
2919 && hl_endpos->col < startpos->col)
2920 hl_endpos->col = startpos->col;
2921 limit_pos(hl_endpos, m_endpos);
2922
2923 /* now the match ends where the highlighting ends, it is turned
2924 * into the matchgroup for the end */
2925 *m_endpos = *hl_endpos;
2926 }
2927 else
2928 {
2929 *end_idx = 0;
2930 *hl_endpos = *end_endpos;
2931 }
2932
2933 *flagsp = spp->sp_flags;
2934
2935 had_match = TRUE;
2936 break;
2937 }
2938
2939 /* no match for an END pattern in this line */
2940 if (!had_match)
2941 m_endpos->lnum = 0;
2942
2943 /* Remove external matches. */
2944 unref_extmatch(re_extmatch_in);
2945 re_extmatch_in = NULL;
2946}
2947
2948/*
2949 * Limit "pos" not to be after "limit".
2950 */
2951 static void
2952limit_pos(pos, limit)
2953 lpos_T *pos;
2954 lpos_T *limit;
2955{
2956 if (pos->lnum > limit->lnum)
2957 *pos = *limit;
2958 else if (pos->lnum == limit->lnum && pos->col > limit->col)
2959 pos->col = limit->col;
2960}
2961
2962/*
2963 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
2964 */
2965 static void
2966limit_pos_zero(pos, limit)
2967 lpos_T *pos;
2968 lpos_T *limit;
2969{
2970 if (pos->lnum == 0)
2971 *pos = *limit;
2972 else
2973 limit_pos(pos, limit);
2974}
2975
2976/*
2977 * Add offset to matched text for end of match or highlight.
2978 */
2979 static void
2980syn_add_end_off(result, regmatch, spp, idx, extra)
2981 lpos_T *result; /* returned position */
2982 regmmatch_T *regmatch; /* start/end of match */
2983 synpat_T *spp; /* matched pattern */
2984 int idx; /* index of offset */
2985 int extra; /* extra chars for offset to start */
2986{
2987 int col;
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00002988 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002989
2990 if (spp->sp_off_flags & (1 << idx))
2991 {
2992 result->lnum = regmatch->startpos[0].lnum;
2993 col = regmatch->startpos[0].col + extra;
2994 }
2995 else
2996 {
2997 result->lnum = regmatch->endpos[0].lnum;
2998 col = regmatch->endpos[0].col;
2999 }
3000 col += spp->sp_offsets[idx];
3001 if (col < 0)
3002 result->col = 0;
3003 else
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003004 {
3005 /* Don't go past the end of the line. Matters for "rs=e+2" when there
Bram Moolenaar33aec762006-01-22 23:30:12 +00003006 * is a matchgroup. Watch out for match with last NL in the buffer. */
3007 if (result->lnum > syn_buf->b_ml.ml_line_count)
3008 len = 0;
3009 else
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003010 len = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003011 if (col > len)
3012 result->col = len;
3013 else
3014 result->col = col;
3015 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003016}
3017
3018/*
3019 * Add offset to matched text for start of match or highlight.
3020 * Avoid resulting column to become negative.
3021 */
3022 static void
3023syn_add_start_off(result, regmatch, spp, idx, extra)
3024 lpos_T *result; /* returned position */
3025 regmmatch_T *regmatch; /* start/end of match */
3026 synpat_T *spp;
3027 int idx;
3028 int extra; /* extra chars for offset to end */
3029{
3030 int col;
3031
3032 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3033 {
3034 result->lnum = regmatch->endpos[0].lnum;
3035 col = regmatch->endpos[0].col + extra;
3036 }
3037 else
3038 {
3039 result->lnum = regmatch->startpos[0].lnum;
3040 col = regmatch->startpos[0].col;
3041 }
3042 col += spp->sp_offsets[idx];
3043 if (col < 0)
3044 result->col = 0;
3045 else
3046 result->col = col;
3047}
3048
3049/*
3050 * Get current line in syntax buffer.
3051 */
3052 static char_u *
3053syn_getcurline()
3054{
3055 return ml_get_buf(syn_buf, current_lnum, FALSE);
3056}
3057
3058/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003059 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003060 * Returns TRUE when there is a match.
3061 */
3062 static int
3063syn_regexec(rmp, lnum, col)
3064 regmmatch_T *rmp;
3065 linenr_T lnum;
3066 colnr_T col;
3067{
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003068 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003069 if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col) > 0)
3070 {
3071 rmp->startpos[0].lnum += lnum;
3072 rmp->endpos[0].lnum += lnum;
3073 return TRUE;
3074 }
3075 return FALSE;
3076}
3077
3078/*
3079 * Check one position in a line for a matching keyword.
3080 * The caller must check if a keyword can start at startcol.
3081 * Return it's ID if found, 0 otherwise.
3082 */
3083 static int
3084check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si)
3085 char_u *line;
3086 int startcol; /* position in line to check for keyword */
3087 int *endcolp; /* return: character after found keyword */
3088 long *flagsp; /* return: flags of matching keyword */
3089 short **next_listp; /* return: next_list of matching keyword */
3090 stateitem_T *cur_si; /* item at the top of the stack */
3091{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003092 keyentry_T *kp;
3093 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003094 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003095 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003096 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003097 hashtab_T *ht;
3098 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003099
3100 /* Find first character after the keyword. First character was already
3101 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003102 kwp = line + startcol;
3103 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003104 do
3105 {
3106#ifdef FEAT_MBYTE
3107 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003108 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003109 else
3110#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003111 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003112 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003113 while (vim_iswordc_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003114
Bram Moolenaardad6b692005-01-25 22:14:34 +00003115 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003116 return 0;
3117
3118 /*
3119 * Must make a copy of the keyword, so we can add a NUL and make it
3120 * lowercase.
3121 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003122 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003123
3124 /*
3125 * Try twice:
3126 * 1. matching case
3127 * 2. ignoring case
3128 */
3129 for (round = 1; round <= 2; ++round)
3130 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003131 ht = round == 1 ? &syn_buf->b_keywtab : &syn_buf->b_keywtab_ic;
3132 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003133 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003134 if (round == 2) /* ignore case */
3135 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003136
3137 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003138 * Find keywords that match. There can be several with different
3139 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003140 * When current_next_list is non-zero accept only that group, otherwise:
3141 * Accept a not-contained keyword at toplevel.
3142 * Accept a keyword at other levels only if it is in the contains list.
3143 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003144 hi = hash_find(ht, keyword);
3145 if (!HASHITEM_EMPTY(hi))
3146 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003147 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003148 if (current_next_list != 0
3149 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3150 : (cur_si == NULL
3151 ? !(kp->flags & HL_CONTAINED)
3152 : in_id_list(cur_si, cur_si->si_cont_list,
3153 &kp->k_syn, kp->flags & HL_CONTAINED)))
3154 {
3155 *endcolp = startcol + kwlen;
3156 *flagsp = kp->flags;
3157 *next_listp = kp->next_list;
3158 return kp->k_syn.id;
3159 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003160 }
3161 }
3162 return 0;
3163}
3164
3165/*
3166 * Handle ":syntax case" command.
3167 */
3168/* ARGSUSED */
3169 static void
3170syn_cmd_case(eap, syncing)
3171 exarg_T *eap;
3172 int syncing; /* not used */
3173{
3174 char_u *arg = eap->arg;
3175 char_u *next;
3176
3177 eap->nextcmd = find_nextcmd(arg);
3178 if (eap->skip)
3179 return;
3180
3181 next = skiptowhite(arg);
3182 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
3183 curbuf->b_syn_ic = FALSE;
3184 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
3185 curbuf->b_syn_ic = TRUE;
3186 else
3187 EMSG2(_("E390: Illegal argument: %s"), arg);
3188}
3189
3190/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003191 * Handle ":syntax spell" command.
3192 */
3193/* ARGSUSED */
3194 static void
3195syn_cmd_spell(eap, syncing)
3196 exarg_T *eap;
3197 int syncing; /* not used */
3198{
3199 char_u *arg = eap->arg;
3200 char_u *next;
3201
3202 eap->nextcmd = find_nextcmd(arg);
3203 if (eap->skip)
3204 return;
3205
3206 next = skiptowhite(arg);
3207 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
3208 curbuf->b_syn_spell = SYNSPL_TOP;
3209 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
3210 curbuf->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003211 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003212 curbuf->b_syn_spell = SYNSPL_DEFAULT;
3213 else
3214 EMSG2(_("E390: Illegal argument: %s"), arg);
3215}
3216
3217/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003218 * Clear all syntax info for one buffer.
3219 */
3220 void
3221syntax_clear(buf)
3222 buf_T *buf;
3223{
3224 int i;
3225
Bram Moolenaar5b8d8fd2005-08-16 23:01:50 +00003226 buf->b_syn_error = FALSE; /* clear previous error */
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003227 buf->b_syn_ic = FALSE; /* Use case, by default */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003228 buf->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003229 buf->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003230
3231 /* free the keywords */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003232 clear_keywtab(&buf->b_keywtab);
3233 clear_keywtab(&buf->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003234
3235 /* free the syntax patterns */
3236 for (i = buf->b_syn_patterns.ga_len; --i >= 0; )
3237 syn_clear_pattern(buf, i);
3238 ga_clear(&buf->b_syn_patterns);
3239
3240 /* free the syntax clusters */
3241 for (i = buf->b_syn_clusters.ga_len; --i >= 0; )
3242 syn_clear_cluster(buf, i);
3243 ga_clear(&buf->b_syn_clusters);
Bram Moolenaar75c50c42005-06-04 22:06:24 +00003244 buf->b_spell_cluster_id = 0;
3245 buf->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003246
3247 buf->b_syn_sync_flags = 0;
3248 buf->b_syn_sync_minlines = 0;
3249 buf->b_syn_sync_maxlines = 0;
3250 buf->b_syn_sync_linebreaks = 0;
3251
3252 vim_free(buf->b_syn_linecont_prog);
3253 buf->b_syn_linecont_prog = NULL;
3254 vim_free(buf->b_syn_linecont_pat);
3255 buf->b_syn_linecont_pat = NULL;
3256#ifdef FEAT_FOLDING
3257 buf->b_syn_folditems = 0;
3258#endif
3259
3260 /* free the stored states */
3261 syn_stack_free_all(buf);
3262 invalidate_current_state();
3263}
3264
3265/*
3266 * Clear syncing info for one buffer.
3267 */
3268 static void
3269syntax_sync_clear()
3270{
3271 int i;
3272
3273 /* free the syntax patterns */
3274 for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
3275 if (SYN_ITEMS(curbuf)[i].sp_syncing)
3276 syn_remove_pattern(curbuf, i);
3277
3278 curbuf->b_syn_sync_flags = 0;
3279 curbuf->b_syn_sync_minlines = 0;
3280 curbuf->b_syn_sync_maxlines = 0;
3281 curbuf->b_syn_sync_linebreaks = 0;
3282
3283 vim_free(curbuf->b_syn_linecont_prog);
3284 curbuf->b_syn_linecont_prog = NULL;
3285 vim_free(curbuf->b_syn_linecont_pat);
3286 curbuf->b_syn_linecont_pat = NULL;
3287
3288 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
3289}
3290
3291/*
3292 * Remove one pattern from the buffer's pattern list.
3293 */
3294 static void
3295syn_remove_pattern(buf, idx)
3296 buf_T *buf;
3297 int idx;
3298{
3299 synpat_T *spp;
3300
3301 spp = &(SYN_ITEMS(buf)[idx]);
3302#ifdef FEAT_FOLDING
3303 if (spp->sp_flags & HL_FOLD)
3304 --buf->b_syn_folditems;
3305#endif
3306 syn_clear_pattern(buf, idx);
3307 mch_memmove(spp, spp + 1,
3308 sizeof(synpat_T) * (buf->b_syn_patterns.ga_len - idx - 1));
3309 --buf->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003310}
3311
3312/*
3313 * Clear and free one syntax pattern. When clearing all, must be called from
3314 * last to first!
3315 */
3316 static void
3317syn_clear_pattern(buf, i)
3318 buf_T *buf;
3319 int i;
3320{
3321 vim_free(SYN_ITEMS(buf)[i].sp_pattern);
3322 vim_free(SYN_ITEMS(buf)[i].sp_prog);
3323 /* Only free sp_cont_list and sp_next_list of first start pattern */
3324 if (i == 0 || SYN_ITEMS(buf)[i - 1].sp_type != SPTYPE_START)
3325 {
3326 vim_free(SYN_ITEMS(buf)[i].sp_cont_list);
3327 vim_free(SYN_ITEMS(buf)[i].sp_next_list);
3328 }
3329}
3330
3331/*
3332 * Clear and free one syntax cluster.
3333 */
3334 static void
3335syn_clear_cluster(buf, i)
3336 buf_T *buf;
3337 int i;
3338{
3339 vim_free(SYN_CLSTR(buf)[i].scl_name);
3340 vim_free(SYN_CLSTR(buf)[i].scl_name_u);
3341 vim_free(SYN_CLSTR(buf)[i].scl_list);
3342}
3343
3344/*
3345 * Handle ":syntax clear" command.
3346 */
3347 static void
3348syn_cmd_clear(eap, syncing)
3349 exarg_T *eap;
3350 int syncing;
3351{
3352 char_u *arg = eap->arg;
3353 char_u *arg_end;
3354 int id;
3355
3356 eap->nextcmd = find_nextcmd(arg);
3357 if (eap->skip)
3358 return;
3359
3360 /*
3361 * We have to disable this within ":syn include @group filename",
3362 * because otherwise @group would get deleted.
3363 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3364 * clear".
3365 */
3366 if (curbuf->b_syn_topgrp != 0)
3367 return;
3368
3369 if (ends_excmd(*arg))
3370 {
3371 /*
3372 * No argument: Clear all syntax items.
3373 */
3374 if (syncing)
3375 syntax_sync_clear();
3376 else
3377 {
3378 syntax_clear(curbuf);
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003379 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003380 }
3381 }
3382 else
3383 {
3384 /*
3385 * Clear the group IDs that are in the argument.
3386 */
3387 while (!ends_excmd(*arg))
3388 {
3389 arg_end = skiptowhite(arg);
3390 if (*arg == '@')
3391 {
3392 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3393 if (id == 0)
3394 {
3395 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3396 break;
3397 }
3398 else
3399 {
3400 /*
3401 * We can't physically delete a cluster without changing
3402 * the IDs of other clusters, so we do the next best thing
3403 * and make it empty.
3404 */
3405 short scl_id = id - SYNID_CLUSTER;
3406
3407 vim_free(SYN_CLSTR(curbuf)[scl_id].scl_list);
3408 SYN_CLSTR(curbuf)[scl_id].scl_list = NULL;
3409 }
3410 }
3411 else
3412 {
3413 id = syn_namen2id(arg, (int)(arg_end - arg));
3414 if (id == 0)
3415 {
3416 EMSG2(_(e_nogroup), arg);
3417 break;
3418 }
3419 else
3420 syn_clear_one(id, syncing);
3421 }
3422 arg = skipwhite(arg_end);
3423 }
3424 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003425 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003426 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
3427}
3428
3429/*
3430 * Clear one syntax group for the current buffer.
3431 */
3432 static void
3433syn_clear_one(id, syncing)
3434 int id;
3435 int syncing;
3436{
3437 synpat_T *spp;
3438 int idx;
3439
3440 /* Clear keywords only when not ":syn sync clear group-name" */
3441 if (!syncing)
3442 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003443 (void)syn_clear_keyword(id, &curbuf->b_keywtab);
3444 (void)syn_clear_keyword(id, &curbuf->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003445 }
3446
3447 /* clear the patterns for "id" */
3448 for (idx = curbuf->b_syn_patterns.ga_len; --idx >= 0; )
3449 {
3450 spp = &(SYN_ITEMS(curbuf)[idx]);
3451 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3452 continue;
3453 syn_remove_pattern(curbuf, idx);
3454 }
3455}
3456
3457/*
3458 * Handle ":syntax on" command.
3459 */
3460/* ARGSUSED */
3461 static void
3462syn_cmd_on(eap, syncing)
3463 exarg_T *eap;
3464 int syncing; /* not used */
3465{
3466 syn_cmd_onoff(eap, "syntax");
3467}
3468
3469/*
3470 * Handle ":syntax enable" command.
3471 */
3472/* ARGSUSED */
3473 static void
3474syn_cmd_enable(eap, syncing)
3475 exarg_T *eap;
3476 int syncing; /* not used */
3477{
3478 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3479 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003480 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003481}
3482
3483/*
3484 * Handle ":syntax reset" command.
3485 */
3486/* ARGSUSED */
3487 static void
3488syn_cmd_reset(eap, syncing)
3489 exarg_T *eap;
3490 int syncing; /* not used */
3491{
3492 eap->nextcmd = check_nextcmd(eap->arg);
3493 if (!eap->skip)
3494 {
3495 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3496 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003497 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003498 }
3499}
3500
3501/*
3502 * Handle ":syntax manual" command.
3503 */
3504/* ARGSUSED */
3505 static void
3506syn_cmd_manual(eap, syncing)
3507 exarg_T *eap;
3508 int syncing; /* not used */
3509{
3510 syn_cmd_onoff(eap, "manual");
3511}
3512
3513/*
3514 * Handle ":syntax off" command.
3515 */
3516/* ARGSUSED */
3517 static void
3518syn_cmd_off(eap, syncing)
3519 exarg_T *eap;
3520 int syncing; /* not used */
3521{
3522 syn_cmd_onoff(eap, "nosyntax");
3523}
3524
3525 static void
3526syn_cmd_onoff(eap, name)
3527 exarg_T *eap;
3528 char *name;
3529{
3530 char_u buf[100];
3531
3532 eap->nextcmd = check_nextcmd(eap->arg);
3533 if (!eap->skip)
3534 {
3535 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003536 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003537 do_cmdline_cmd(buf);
3538 }
3539}
3540
3541/*
3542 * Handle ":syntax [list]" command: list current syntax words.
3543 */
3544 static void
3545syn_cmd_list(eap, syncing)
3546 exarg_T *eap;
3547 int syncing; /* when TRUE: list syncing items */
3548{
3549 char_u *arg = eap->arg;
3550 int id;
3551 char_u *arg_end;
3552
3553 eap->nextcmd = find_nextcmd(arg);
3554 if (eap->skip)
3555 return;
3556
3557 if (!syntax_present(curbuf))
3558 {
3559 MSG(_("No Syntax items defined for this buffer"));
3560 return;
3561 }
3562
3563 if (syncing)
3564 {
3565 if (curbuf->b_syn_sync_flags & SF_CCOMMENT)
3566 {
3567 MSG_PUTS(_("syncing on C-style comments"));
3568 syn_lines_msg();
3569 syn_match_msg();
3570 return;
3571 }
3572 else if (!(curbuf->b_syn_sync_flags & SF_MATCH))
3573 {
3574 if (curbuf->b_syn_sync_minlines == 0)
3575 MSG_PUTS(_("no syncing"));
3576 else
3577 {
3578 MSG_PUTS(_("syncing starts "));
3579 msg_outnum(curbuf->b_syn_sync_minlines);
3580 MSG_PUTS(_(" lines before top line"));
3581 syn_match_msg();
3582 }
3583 return;
3584 }
3585 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
3586 if (curbuf->b_syn_sync_minlines > 0
3587 || curbuf->b_syn_sync_maxlines > 0
3588 || curbuf->b_syn_sync_linebreaks > 0)
3589 {
3590 MSG_PUTS(_("\nsyncing on items"));
3591 syn_lines_msg();
3592 syn_match_msg();
3593 }
3594 }
3595 else
3596 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3597 if (ends_excmd(*arg))
3598 {
3599 /*
3600 * No argument: List all group IDs and all syntax clusters.
3601 */
3602 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3603 syn_list_one(id, syncing, FALSE);
3604 for (id = 0; id < curbuf->b_syn_clusters.ga_len && !got_int; ++id)
3605 syn_list_cluster(id);
3606 }
3607 else
3608 {
3609 /*
3610 * List the group IDs and syntax clusters that are in the argument.
3611 */
3612 while (!ends_excmd(*arg) && !got_int)
3613 {
3614 arg_end = skiptowhite(arg);
3615 if (*arg == '@')
3616 {
3617 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3618 if (id == 0)
3619 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3620 else
3621 syn_list_cluster(id - SYNID_CLUSTER);
3622 }
3623 else
3624 {
3625 id = syn_namen2id(arg, (int)(arg_end - arg));
3626 if (id == 0)
3627 EMSG2(_(e_nogroup), arg);
3628 else
3629 syn_list_one(id, syncing, TRUE);
3630 }
3631 arg = skipwhite(arg_end);
3632 }
3633 }
3634 eap->nextcmd = check_nextcmd(arg);
3635}
3636
3637 static void
3638syn_lines_msg()
3639{
3640 if (curbuf->b_syn_sync_maxlines > 0 || curbuf->b_syn_sync_minlines > 0)
3641 {
3642 MSG_PUTS("; ");
3643 if (curbuf->b_syn_sync_minlines > 0)
3644 {
3645 MSG_PUTS(_("minimal "));
3646 msg_outnum(curbuf->b_syn_sync_minlines);
3647 if (curbuf->b_syn_sync_maxlines)
3648 MSG_PUTS(", ");
3649 }
3650 if (curbuf->b_syn_sync_maxlines > 0)
3651 {
3652 MSG_PUTS(_("maximal "));
3653 msg_outnum(curbuf->b_syn_sync_maxlines);
3654 }
3655 MSG_PUTS(_(" lines before top line"));
3656 }
3657}
3658
3659 static void
3660syn_match_msg()
3661{
3662 if (curbuf->b_syn_sync_linebreaks > 0)
3663 {
3664 MSG_PUTS(_("; match "));
3665 msg_outnum(curbuf->b_syn_sync_linebreaks);
3666 MSG_PUTS(_(" line breaks"));
3667 }
3668}
3669
3670static int last_matchgroup;
3671
3672struct name_list
3673{
3674 int flag;
3675 char *name;
3676};
3677
3678static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3679
3680/*
3681 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3682 */
3683 static void
3684syn_list_one(id, syncing, link_only)
3685 int id;
3686 int syncing; /* when TRUE: list syncing items */
3687 int link_only; /* when TRUE; list link-only too */
3688{
3689 int attr;
3690 int idx;
3691 int did_header = FALSE;
3692 synpat_T *spp;
3693 static struct name_list namelist1[] =
3694 {
3695 {HL_DISPLAY, "display"},
3696 {HL_CONTAINED, "contained"},
3697 {HL_ONELINE, "oneline"},
3698 {HL_KEEPEND, "keepend"},
3699 {HL_EXTEND, "extend"},
3700 {HL_EXCLUDENL, "excludenl"},
3701 {HL_TRANSP, "transparent"},
3702 {HL_FOLD, "fold"},
3703 {0, NULL}
3704 };
3705 static struct name_list namelist2[] =
3706 {
3707 {HL_SKIPWHITE, "skipwhite"},
3708 {HL_SKIPNL, "skipnl"},
3709 {HL_SKIPEMPTY, "skipempty"},
3710 {0, NULL}
3711 };
3712
3713 attr = hl_attr(HLF_D); /* highlight like directories */
3714
3715 /* list the keywords for "id" */
3716 if (!syncing)
3717 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003718 did_header = syn_list_keywords(id, &curbuf->b_keywtab, FALSE, attr);
3719 did_header = syn_list_keywords(id, &curbuf->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003720 did_header, attr);
3721 }
3722
3723 /* list the patterns for "id" */
3724 for (idx = 0; idx < curbuf->b_syn_patterns.ga_len && !got_int; ++idx)
3725 {
3726 spp = &(SYN_ITEMS(curbuf)[idx]);
3727 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3728 continue;
3729
3730 (void)syn_list_header(did_header, 999, id);
3731 did_header = TRUE;
3732 last_matchgroup = 0;
3733 if (spp->sp_type == SPTYPE_MATCH)
3734 {
3735 put_pattern("match", ' ', spp, attr);
3736 msg_putchar(' ');
3737 }
3738 else if (spp->sp_type == SPTYPE_START)
3739 {
3740 while (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_START)
3741 put_pattern("start", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3742 if (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_SKIP)
3743 put_pattern("skip", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3744 while (idx < curbuf->b_syn_patterns.ga_len
3745 && SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_END)
3746 put_pattern("end", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3747 --idx;
3748 msg_putchar(' ');
3749 }
3750 syn_list_flags(namelist1, spp->sp_flags, attr);
3751
3752 if (spp->sp_cont_list != NULL)
3753 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3754
3755 if (spp->sp_syn.cont_in_list != NULL)
3756 put_id_list((char_u *)"containedin",
3757 spp->sp_syn.cont_in_list, attr);
3758
3759 if (spp->sp_next_list != NULL)
3760 {
3761 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3762 syn_list_flags(namelist2, spp->sp_flags, attr);
3763 }
3764 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3765 {
3766 if (spp->sp_flags & HL_SYNC_HERE)
3767 msg_puts_attr((char_u *)"grouphere", attr);
3768 else
3769 msg_puts_attr((char_u *)"groupthere", attr);
3770 msg_putchar(' ');
3771 if (spp->sp_sync_idx >= 0)
3772 msg_outtrans(HL_TABLE()[SYN_ITEMS(curbuf)
3773 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3774 else
3775 MSG_PUTS("NONE");
3776 msg_putchar(' ');
3777 }
3778 }
3779
3780 /* list the link, if there is one */
3781 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3782 {
3783 (void)syn_list_header(did_header, 999, id);
3784 msg_puts_attr((char_u *)"links to", attr);
3785 msg_putchar(' ');
3786 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3787 }
3788}
3789
3790 static void
3791syn_list_flags(nl, flags, attr)
3792 struct name_list *nl;
3793 int flags;
3794 int attr;
3795{
3796 int i;
3797
3798 for (i = 0; nl[i].flag != 0; ++i)
3799 if (flags & nl[i].flag)
3800 {
3801 msg_puts_attr((char_u *)nl[i].name, attr);
3802 msg_putchar(' ');
3803 }
3804}
3805
3806/*
3807 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
3808 */
3809 static void
3810syn_list_cluster(id)
3811 int id;
3812{
3813 int endcol = 15;
3814
3815 /* slight hack: roughly duplicate the guts of syn_list_header() */
3816 msg_putchar('\n');
3817 msg_outtrans(SYN_CLSTR(curbuf)[id].scl_name);
3818
3819 if (msg_col >= endcol) /* output at least one space */
3820 endcol = msg_col + 1;
3821 if (Columns <= endcol) /* avoid hang for tiny window */
3822 endcol = Columns - 1;
3823
3824 msg_advance(endcol);
3825 if (SYN_CLSTR(curbuf)[id].scl_list != NULL)
3826 {
3827 put_id_list((char_u *)"cluster", SYN_CLSTR(curbuf)[id].scl_list,
3828 hl_attr(HLF_D));
3829 }
3830 else
3831 {
3832 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
3833 msg_puts((char_u *)"=NONE");
3834 }
3835}
3836
3837 static void
3838put_id_list(name, list, attr)
3839 char_u *name;
3840 short *list;
3841 int attr;
3842{
3843 short *p;
3844
3845 msg_puts_attr(name, attr);
3846 msg_putchar('=');
3847 for (p = list; *p; ++p)
3848 {
3849 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
3850 {
3851 if (p[1])
3852 MSG_PUTS("ALLBUT");
3853 else
3854 MSG_PUTS("ALL");
3855 }
3856 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
3857 {
3858 MSG_PUTS("TOP");
3859 }
3860 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
3861 {
3862 MSG_PUTS("CONTAINED");
3863 }
3864 else if (*p >= SYNID_CLUSTER)
3865 {
3866 short scl_id = *p - SYNID_CLUSTER;
3867
3868 msg_putchar('@');
3869 msg_outtrans(SYN_CLSTR(curbuf)[scl_id].scl_name);
3870 }
3871 else
3872 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
3873 if (p[1])
3874 msg_putchar(',');
3875 }
3876 msg_putchar(' ');
3877}
3878
3879 static void
3880put_pattern(s, c, spp, attr)
3881 char *s;
3882 int c;
3883 synpat_T *spp;
3884 int attr;
3885{
3886 long n;
3887 int mask;
3888 int first;
3889 static char *sepchars = "/+=-#@\"|'^&";
3890 int i;
3891
3892 /* May have to write "matchgroup=group" */
3893 if (last_matchgroup != spp->sp_syn_match_id)
3894 {
3895 last_matchgroup = spp->sp_syn_match_id;
3896 msg_puts_attr((char_u *)"matchgroup", attr);
3897 msg_putchar('=');
3898 if (last_matchgroup == 0)
3899 msg_outtrans((char_u *)"NONE");
3900 else
3901 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
3902 msg_putchar(' ');
3903 }
3904
3905 /* output the name of the pattern and an '=' or ' ' */
3906 msg_puts_attr((char_u *)s, attr);
3907 msg_putchar(c);
3908
3909 /* output the pattern, in between a char that is not in the pattern */
3910 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
3911 if (sepchars[++i] == NUL)
3912 {
3913 i = 0; /* no good char found, just use the first one */
3914 break;
3915 }
3916 msg_putchar(sepchars[i]);
3917 msg_outtrans(spp->sp_pattern);
3918 msg_putchar(sepchars[i]);
3919
3920 /* output any pattern options */
3921 first = TRUE;
3922 for (i = 0; i < SPO_COUNT; ++i)
3923 {
3924 mask = (1 << i);
3925 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
3926 {
3927 if (!first)
3928 msg_putchar(','); /* separate with commas */
3929 msg_puts((char_u *)spo_name_tab[i]);
3930 n = spp->sp_offsets[i];
3931 if (i != SPO_LC_OFF)
3932 {
3933 if (spp->sp_off_flags & mask)
3934 msg_putchar('s');
3935 else
3936 msg_putchar('e');
3937 if (n > 0)
3938 msg_putchar('+');
3939 }
3940 if (n || i == SPO_LC_OFF)
3941 msg_outnum(n);
3942 first = FALSE;
3943 }
3944 }
3945 msg_putchar(' ');
3946}
3947
3948/*
3949 * List or clear the keywords for one syntax group.
3950 * Return TRUE if the header has been printed.
3951 */
3952 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00003953syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003954 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003955 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003956 int did_header; /* header has already been printed */
3957 int attr;
3958{
Bram Moolenaar071d4272004-06-13 20:20:40 +00003959 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003960 hashitem_T *hi;
3961 keyentry_T *kp;
3962 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003963 int prev_contained = 0;
3964 short *prev_next_list = NULL;
3965 short *prev_cont_in_list = NULL;
3966 int prev_skipnl = 0;
3967 int prev_skipwhite = 0;
3968 int prev_skipempty = 0;
3969
Bram Moolenaar071d4272004-06-13 20:20:40 +00003970 /*
3971 * Unfortunately, this list of keywords is not sorted on alphabet but on
3972 * hash value...
3973 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003974 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003975 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003976 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003977 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003978 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003979 --todo;
3980 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003981 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003982 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003983 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003984 if (prev_contained != (kp->flags & HL_CONTAINED)
3985 || prev_skipnl != (kp->flags & HL_SKIPNL)
3986 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
3987 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
3988 || prev_cont_in_list != kp->k_syn.cont_in_list
3989 || prev_next_list != kp->next_list)
3990 outlen = 9999;
3991 else
3992 outlen = (int)STRLEN(kp->keyword);
3993 /* output "contained" and "nextgroup" on each line */
3994 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003995 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003996 prev_contained = 0;
3997 prev_next_list = NULL;
3998 prev_cont_in_list = NULL;
3999 prev_skipnl = 0;
4000 prev_skipwhite = 0;
4001 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004002 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004003 did_header = TRUE;
4004 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004005 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004006 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004007 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004008 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004009 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004010 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004011 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004012 put_id_list((char_u *)"containedin",
4013 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004014 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004015 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004016 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004017 if (kp->next_list != prev_next_list)
4018 {
4019 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4020 msg_putchar(' ');
4021 prev_next_list = kp->next_list;
4022 if (kp->flags & HL_SKIPNL)
4023 {
4024 msg_puts_attr((char_u *)"skipnl", attr);
4025 msg_putchar(' ');
4026 prev_skipnl = (kp->flags & HL_SKIPNL);
4027 }
4028 if (kp->flags & HL_SKIPWHITE)
4029 {
4030 msg_puts_attr((char_u *)"skipwhite", attr);
4031 msg_putchar(' ');
4032 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4033 }
4034 if (kp->flags & HL_SKIPEMPTY)
4035 {
4036 msg_puts_attr((char_u *)"skipempty", attr);
4037 msg_putchar(' ');
4038 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4039 }
4040 }
4041 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004042 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004043 }
4044 }
4045 }
4046
4047 return did_header;
4048}
4049
4050 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004051syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004052 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004053 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004054{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004055 hashitem_T *hi;
4056 keyentry_T *kp;
4057 keyentry_T *kp_prev;
4058 keyentry_T *kp_next;
4059 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004060
Bram Moolenaardad6b692005-01-25 22:14:34 +00004061 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004062 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004063 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004064 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004065 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004066 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004067 --todo;
4068 kp_prev = NULL;
4069 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004070 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004071 if (kp->k_syn.id == id)
4072 {
4073 kp_next = kp->ke_next;
4074 if (kp_prev == NULL)
4075 {
4076 if (kp_next == NULL)
4077 hash_remove(ht, hi);
4078 else
4079 hi->hi_key = KE2HIKEY(kp_next);
4080 }
4081 else
4082 kp_prev->ke_next = kp_next;
4083 vim_free(kp->next_list);
4084 vim_free(kp->k_syn.cont_in_list);
4085 vim_free(kp);
4086 kp = kp_next;
4087 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004088 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004089 {
4090 kp_prev = kp;
4091 kp = kp->ke_next;
4092 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004093 }
4094 }
4095 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004096 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004097}
4098
4099/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004100 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004101 */
4102 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004103clear_keywtab(ht)
4104 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004105{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004106 hashitem_T *hi;
4107 int todo;
4108 keyentry_T *kp;
4109 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004110
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004111 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004112 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004113 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004114 if (!HASHITEM_EMPTY(hi))
4115 {
4116 --todo;
4117 kp = HI2KE(hi);
4118 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004119 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004120 kp_next = kp->ke_next;
4121 vim_free(kp->next_list);
4122 vim_free(kp->k_syn.cont_in_list);
4123 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004124 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004125 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004126 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004127 hash_clear(ht);
4128 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004129}
4130
4131/*
4132 * Add a keyword to the list of keywords.
4133 */
4134 static void
4135add_keyword(name, id, flags, cont_in_list, next_list)
4136 char_u *name; /* name of keyword */
4137 int id; /* group ID for this keyword */
4138 int flags; /* flags for this keyword */
4139 short *cont_in_list; /* containedin for this keyword */
4140 short *next_list; /* nextgroup for this keyword */
4141{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004142 keyentry_T *kp;
4143 hashtab_T *ht;
4144 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004145 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004146 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004147 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004148
4149 if (curbuf->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004150 name_ic = str_foldcase(name, (int)STRLEN(name),
4151 name_folded, MAXKEYWLEN + 1);
4152 else
4153 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004154 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4155 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004156 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004157 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004158 kp->k_syn.id = id;
4159 kp->k_syn.inc_tag = current_syn_inc_tag;
4160 kp->flags = flags;
4161 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004162 if (cont_in_list != NULL)
4163 curbuf->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004164 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004165
4166 if (curbuf->b_syn_ic)
Bram Moolenaardad6b692005-01-25 22:14:34 +00004167 ht = &curbuf->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004168 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004169 ht = &curbuf->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004170
Bram Moolenaardad6b692005-01-25 22:14:34 +00004171 hash = hash_hash(kp->keyword);
4172 hi = hash_lookup(ht, kp->keyword, hash);
4173 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004174 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004175 /* new keyword, add to hashtable */
4176 kp->ke_next = NULL;
4177 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004178 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004179 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004180 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004181 /* keyword already exists, prepend to list */
4182 kp->ke_next = HI2KE(hi);
4183 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004184 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004185}
4186
4187/*
4188 * Get the start and end of the group name argument.
4189 * Return a pointer to the first argument.
4190 * Return NULL if the end of the command was found instead of further args.
4191 */
4192 static char_u *
4193get_group_name(arg, name_end)
4194 char_u *arg; /* start of the argument */
4195 char_u **name_end; /* pointer to end of the name */
4196{
4197 char_u *rest;
4198
4199 *name_end = skiptowhite(arg);
4200 rest = skipwhite(*name_end);
4201
4202 /*
4203 * Check if there are enough arguments. The first argument may be a
4204 * pattern, where '|' is allowed, so only check for NUL.
4205 */
4206 if (ends_excmd(*arg) || *rest == NUL)
4207 return NULL;
4208 return rest;
4209}
4210
4211/*
4212 * Check for syntax command option arguments.
4213 * This can be called at any place in the list of arguments, and just picks
4214 * out the arguments that are known. Can be called several times in a row to
4215 * collect all options in between other arguments.
4216 * Return a pointer to the next argument (which isn't an option).
4217 * Return NULL for any error;
4218 */
4219 static char_u *
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004220get_syn_options(arg, opt)
4221 char_u *arg; /* next argument to be checked */
4222 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004223{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004224 char_u *gname_start, *gname;
4225 int syn_id;
4226 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004227 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004228 int i;
4229 int fidx;
4230 static struct flag
4231 {
4232 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004233 int argtype;
4234 int flags;
4235 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4236 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4237 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4238 {"eExXtTeEnNdD", 0, HL_EXTEND},
4239 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4240 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4241 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4242 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4243 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4244 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4245 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4246 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4247 {"fFoOlLdD", 0, HL_FOLD},
4248 {"cCoOnNtTaAiInNsS", 1, 0},
4249 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4250 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004251 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004252 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004253
4254 if (arg == NULL) /* already detected error */
4255 return NULL;
4256
Bram Moolenaar071d4272004-06-13 20:20:40 +00004257 for (;;)
4258 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004259 /*
4260 * This is used very often when a large number of keywords is defined.
4261 * Need to skip quickly when no option name is found.
4262 * Also avoid tolower(), it's slow.
4263 */
4264 if (strchr(first_letters, *arg) == NULL)
4265 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004266
4267 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4268 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004269 p = flagtab[fidx].name;
4270 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4271 if (arg[len] != p[i] && arg[len] != p[i + 1])
4272 break;
4273 if (p[i] == NUL && (vim_iswhite(arg[len])
4274 || (flagtab[fidx].argtype > 0
4275 ? arg[len] == '='
4276 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004277 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004278 if (opt->keyword
4279 && (flagtab[fidx].flags == HL_DISPLAY
4280 || flagtab[fidx].flags == HL_FOLD
4281 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004282 /* treat "display", "fold" and "extend" as a keyword */
4283 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004284 break;
4285 }
4286 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004287 if (fidx < 0) /* no match found */
4288 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004289
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004290 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004291 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004292 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004293 {
4294 EMSG(_("E395: contains argument not accepted here"));
4295 return NULL;
4296 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004297 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004298 return NULL;
4299 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004300 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004301 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004302#if 0 /* cannot happen */
4303 if (opt->cont_in_list == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004304 {
4305 EMSG(_("E396: containedin argument not accepted here"));
4306 return NULL;
4307 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004308#endif
4309 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004310 return NULL;
4311 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004312 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004313 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004314 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004315 return NULL;
4316 }
4317 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004318 {
4319 opt->flags |= flagtab[fidx].flags;
4320 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004321
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004322 if (flagtab[fidx].flags == HL_SYNC_HERE
4323 || flagtab[fidx].flags == HL_SYNC_THERE)
4324 {
4325 if (opt->sync_idx == NULL)
4326 {
4327 EMSG(_("E393: group[t]here not accepted here"));
4328 return NULL;
4329 }
4330 gname_start = arg;
4331 arg = skiptowhite(arg);
4332 if (gname_start == arg)
4333 return NULL;
4334 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4335 if (gname == NULL)
4336 return NULL;
4337 if (STRCMP(gname, "NONE") == 0)
4338 *opt->sync_idx = NONE_IDX;
4339 else
4340 {
4341 syn_id = syn_name2id(gname);
4342 for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
4343 if (SYN_ITEMS(curbuf)[i].sp_syn.id == syn_id
4344 && SYN_ITEMS(curbuf)[i].sp_type == SPTYPE_START)
4345 {
4346 *opt->sync_idx = i;
4347 break;
4348 }
4349 if (i < 0)
4350 {
4351 EMSG2(_("E394: Didn't find region item for %s"), gname);
4352 vim_free(gname);
4353 return NULL;
4354 }
4355 }
4356
4357 vim_free(gname);
4358 arg = skipwhite(arg);
4359 }
4360#ifdef FEAT_FOLDING
4361 else if (flagtab[fidx].flags == HL_FOLD
4362 && foldmethodIsSyntax(curwin))
4363 /* Need to update folds later. */
4364 foldUpdateAll(curwin);
4365#endif
4366 }
4367 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004368
4369 return arg;
4370}
4371
4372/*
4373 * Adjustments to syntax item when declared in a ":syn include"'d file.
4374 * Set the contained flag, and if the item is not already contained, add it
4375 * to the specified top-level group, if any.
4376 */
4377 static void
4378syn_incl_toplevel(id, flagsp)
4379 int id;
4380 int *flagsp;
4381{
4382 if ((*flagsp & HL_CONTAINED) || curbuf->b_syn_topgrp == 0)
4383 return;
4384 *flagsp |= HL_CONTAINED;
4385 if (curbuf->b_syn_topgrp >= SYNID_CLUSTER)
4386 {
4387 /* We have to alloc this, because syn_combine_list() will free it. */
4388 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
4389 int tlg_id = curbuf->b_syn_topgrp - SYNID_CLUSTER;
4390
4391 if (grp_list != NULL)
4392 {
4393 grp_list[0] = id;
4394 grp_list[1] = 0;
4395 syn_combine_list(&SYN_CLSTR(curbuf)[tlg_id].scl_list, &grp_list,
4396 CLUSTER_ADD);
4397 }
4398 }
4399}
4400
4401/*
4402 * Handle ":syntax include [@{group-name}] filename" command.
4403 */
4404/* ARGSUSED */
4405 static void
4406syn_cmd_include(eap, syncing)
4407 exarg_T *eap;
4408 int syncing; /* not used */
4409{
4410 char_u *arg = eap->arg;
4411 int sgl_id = 1;
4412 char_u *group_name_end;
4413 char_u *rest;
4414 char_u *errormsg = NULL;
4415 int prev_toplvl_grp;
4416 int prev_syn_inc_tag;
4417 int source = FALSE;
4418
4419 eap->nextcmd = find_nextcmd(arg);
4420 if (eap->skip)
4421 return;
4422
4423 if (arg[0] == '@')
4424 {
4425 ++arg;
4426 rest = get_group_name(arg, &group_name_end);
4427 if (rest == NULL)
4428 {
4429 EMSG((char_u *)_("E397: Filename required"));
4430 return;
4431 }
4432 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4433 /* separate_nextcmd() and expand_filename() depend on this */
4434 eap->arg = rest;
4435 }
4436
4437 /*
4438 * Everything that's left, up to the next command, should be the
4439 * filename to include.
4440 */
4441 eap->argt |= (XFILE | NOSPC);
4442 separate_nextcmd(eap);
4443 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4444 {
4445 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4446 * file. Need to expand the file name first. In other cases
4447 * ":runtime!" is used. */
4448 source = TRUE;
4449 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4450 {
4451 if (errormsg != NULL)
4452 EMSG(errormsg);
4453 return;
4454 }
4455 }
4456
4457 /*
4458 * Save and restore the existing top-level grouplist id and ":syn
4459 * include" tag around the actual inclusion.
4460 */
4461 prev_syn_inc_tag = current_syn_inc_tag;
4462 current_syn_inc_tag = ++running_syn_inc_tag;
4463 prev_toplvl_grp = curbuf->b_syn_topgrp;
4464 curbuf->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004465 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4466 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004467 EMSG2(_(e_notopen), eap->arg);
4468 curbuf->b_syn_topgrp = prev_toplvl_grp;
4469 current_syn_inc_tag = prev_syn_inc_tag;
4470}
4471
4472/*
4473 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4474 */
4475/* ARGSUSED */
4476 static void
4477syn_cmd_keyword(eap, syncing)
4478 exarg_T *eap;
4479 int syncing; /* not used */
4480{
4481 char_u *arg = eap->arg;
4482 char_u *group_name_end;
4483 int syn_id;
4484 char_u *rest;
4485 char_u *keyword_copy;
4486 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004487 char_u *kw;
4488 syn_opt_arg_T syn_opt_arg;
4489 int cnt;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004490
4491 rest = get_group_name(arg, &group_name_end);
4492
4493 if (rest != NULL)
4494 {
4495 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4496
4497 /* allocate a buffer, for removing the backslashes in the keyword */
4498 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4499 if (keyword_copy != NULL)
4500 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004501 syn_opt_arg.flags = 0;
4502 syn_opt_arg.keyword = TRUE;
4503 syn_opt_arg.sync_idx = NULL;
4504 syn_opt_arg.has_cont_list = FALSE;
4505 syn_opt_arg.cont_in_list = NULL;
4506 syn_opt_arg.next_list = NULL;
4507
Bram Moolenaar071d4272004-06-13 20:20:40 +00004508 /*
4509 * The options given apply to ALL keywords, so all options must be
4510 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004511 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004512 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004513 cnt = 0;
4514 p = keyword_copy;
4515 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004516 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004517 rest = get_syn_options(rest, &syn_opt_arg);
4518 if (rest == NULL || ends_excmd(*rest))
4519 break;
4520 /* Copy the keyword, removing backslashes, and add a NUL. */
4521 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004522 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004523 if (*rest == '\\' && rest[1] != NUL)
4524 ++rest;
4525 *p++ = *rest++;
4526 }
4527 *p++ = NUL;
4528 ++cnt;
4529 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004530
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004531 if (!eap->skip)
4532 {
4533 /* Adjust flags for use of ":syn include". */
4534 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4535
4536 /*
4537 * 2: Add an entry for each keyword.
4538 */
4539 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4540 {
4541 for (p = vim_strchr(kw, '['); ; )
4542 {
4543 if (p != NULL)
4544 *p = NUL;
4545 add_keyword(kw, syn_id, syn_opt_arg.flags,
4546 syn_opt_arg.cont_in_list,
4547 syn_opt_arg.next_list);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004548 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004549 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004550 if (p[1] == NUL)
4551 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004552 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004553 kw = p + 2; /* skip over the NUL */
4554 break;
4555 }
4556 if (p[1] == ']')
4557 {
4558 kw = p + 1; /* skip over the "]" */
4559 break;
4560 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004561#ifdef FEAT_MBYTE
4562 if (has_mbyte)
4563 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004564 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004565
4566 mch_memmove(p, p + 1, l);
4567 p += l;
4568 }
4569 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004570#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004571 {
4572 p[0] = p[1];
4573 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004574 }
4575 }
4576 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004577 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004578
Bram Moolenaar071d4272004-06-13 20:20:40 +00004579 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004580 vim_free(syn_opt_arg.cont_in_list);
4581 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004582 }
4583 }
4584
4585 if (rest != NULL)
4586 eap->nextcmd = check_nextcmd(rest);
4587 else
4588 EMSG2(_(e_invarg2), arg);
4589
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004590 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004591 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
4592}
4593
4594/*
4595 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4596 *
4597 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4598 */
4599 static void
4600syn_cmd_match(eap, syncing)
4601 exarg_T *eap;
4602 int syncing; /* TRUE for ":syntax sync match .. " */
4603{
4604 char_u *arg = eap->arg;
4605 char_u *group_name_end;
4606 char_u *rest;
4607 synpat_T item; /* the item found in the line */
4608 int syn_id;
4609 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004610 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004611 int sync_idx = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004612
4613 /* Isolate the group name, check for validity */
4614 rest = get_group_name(arg, &group_name_end);
4615
4616 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004617 syn_opt_arg.flags = 0;
4618 syn_opt_arg.keyword = FALSE;
4619 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4620 syn_opt_arg.has_cont_list = TRUE;
4621 syn_opt_arg.cont_list = NULL;
4622 syn_opt_arg.cont_in_list = NULL;
4623 syn_opt_arg.next_list = NULL;
4624 rest = get_syn_options(rest, &syn_opt_arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004625
4626 /* get the pattern. */
4627 init_syn_patterns();
4628 vim_memset(&item, 0, sizeof(item));
4629 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004630 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4631 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004632
4633 /* Get options after the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004634 rest = get_syn_options(rest, &syn_opt_arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004635
4636 if (rest != NULL) /* all arguments are valid */
4637 {
4638 /*
4639 * Check for trailing command and illegal trailing arguments.
4640 */
4641 eap->nextcmd = check_nextcmd(rest);
4642 if (!ends_excmd(*rest) || eap->skip)
4643 rest = NULL;
4644 else if (ga_grow(&curbuf->b_syn_patterns, 1) != FAIL
4645 && (syn_id = syn_check_group(arg,
4646 (int)(group_name_end - arg))) != 0)
4647 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004648 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004649 /*
4650 * Store the pattern in the syn_items list
4651 */
4652 idx = curbuf->b_syn_patterns.ga_len;
4653 SYN_ITEMS(curbuf)[idx] = item;
4654 SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4655 SYN_ITEMS(curbuf)[idx].sp_type = SPTYPE_MATCH;
4656 SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4657 SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004658 SYN_ITEMS(curbuf)[idx].sp_flags = syn_opt_arg.flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004659 SYN_ITEMS(curbuf)[idx].sp_sync_idx = sync_idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004660 SYN_ITEMS(curbuf)[idx].sp_cont_list = syn_opt_arg.cont_list;
4661 SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
4662 syn_opt_arg.cont_in_list;
4663 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004664 curbuf->b_syn_containedin = TRUE;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004665 SYN_ITEMS(curbuf)[idx].sp_next_list = syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004666 ++curbuf->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004667
4668 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004669 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004670 curbuf->b_syn_sync_flags |= SF_MATCH;
4671#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004672 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004673 ++curbuf->b_syn_folditems;
4674#endif
4675
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004676 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004677 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
4678 return; /* don't free the progs and patterns now */
4679 }
4680 }
4681
4682 /*
4683 * Something failed, free the allocated memory.
4684 */
4685 vim_free(item.sp_prog);
4686 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004687 vim_free(syn_opt_arg.cont_list);
4688 vim_free(syn_opt_arg.cont_in_list);
4689 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004690
4691 if (rest == NULL)
4692 EMSG2(_(e_invarg2), arg);
4693}
4694
4695/*
4696 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4697 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4698 */
4699 static void
4700syn_cmd_region(eap, syncing)
4701 exarg_T *eap;
4702 int syncing; /* TRUE for ":syntax sync region .." */
4703{
4704 char_u *arg = eap->arg;
4705 char_u *group_name_end;
4706 char_u *rest; /* next arg, NULL on error */
4707 char_u *key_end;
4708 char_u *key = NULL;
4709 char_u *p;
4710 int item;
4711#define ITEM_START 0
4712#define ITEM_SKIP 1
4713#define ITEM_END 2
4714#define ITEM_MATCHGROUP 3
4715 struct pat_ptr
4716 {
4717 synpat_T *pp_synp; /* pointer to syn_pattern */
4718 int pp_matchgroup_id; /* matchgroup ID */
4719 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4720 } *(pat_ptrs[3]);
4721 /* patterns found in the line */
4722 struct pat_ptr *ppp;
4723 struct pat_ptr *ppp_next;
4724 int pat_count = 0; /* nr of syn_patterns found */
4725 int syn_id;
4726 int matchgroup_id = 0;
4727 int not_enough = FALSE; /* not enough arguments */
4728 int illegal = FALSE; /* illegal arguments */
4729 int success = FALSE;
4730 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004731 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004732
4733 /* Isolate the group name, check for validity */
4734 rest = get_group_name(arg, &group_name_end);
4735
4736 pat_ptrs[0] = NULL;
4737 pat_ptrs[1] = NULL;
4738 pat_ptrs[2] = NULL;
4739
4740 init_syn_patterns();
4741
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004742 syn_opt_arg.flags = 0;
4743 syn_opt_arg.keyword = FALSE;
4744 syn_opt_arg.sync_idx = NULL;
4745 syn_opt_arg.has_cont_list = TRUE;
4746 syn_opt_arg.cont_list = NULL;
4747 syn_opt_arg.cont_in_list = NULL;
4748 syn_opt_arg.next_list = NULL;
4749
Bram Moolenaar071d4272004-06-13 20:20:40 +00004750 /*
4751 * get the options, patterns and matchgroup.
4752 */
4753 while (rest != NULL && !ends_excmd(*rest))
4754 {
4755 /* Check for option arguments */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004756 rest = get_syn_options(rest, &syn_opt_arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004757 if (rest == NULL || ends_excmd(*rest))
4758 break;
4759
4760 /* must be a pattern or matchgroup then */
4761 key_end = rest;
4762 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4763 ++key_end;
4764 vim_free(key);
4765 key = vim_strnsave_up(rest, (int)(key_end - rest));
4766 if (key == NULL) /* out of memory */
4767 {
4768 rest = NULL;
4769 break;
4770 }
4771 if (STRCMP(key, "MATCHGROUP") == 0)
4772 item = ITEM_MATCHGROUP;
4773 else if (STRCMP(key, "START") == 0)
4774 item = ITEM_START;
4775 else if (STRCMP(key, "END") == 0)
4776 item = ITEM_END;
4777 else if (STRCMP(key, "SKIP") == 0)
4778 {
4779 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
4780 {
4781 illegal = TRUE;
4782 break;
4783 }
4784 item = ITEM_SKIP;
4785 }
4786 else
4787 break;
4788 rest = skipwhite(key_end);
4789 if (*rest != '=')
4790 {
4791 rest = NULL;
4792 EMSG2(_("E398: Missing '=': %s"), arg);
4793 break;
4794 }
4795 rest = skipwhite(rest + 1);
4796 if (*rest == NUL)
4797 {
4798 not_enough = TRUE;
4799 break;
4800 }
4801
4802 if (item == ITEM_MATCHGROUP)
4803 {
4804 p = skiptowhite(rest);
4805 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
4806 matchgroup_id = 0;
4807 else
4808 {
4809 matchgroup_id = syn_check_group(rest, (int)(p - rest));
4810 if (matchgroup_id == 0)
4811 {
4812 illegal = TRUE;
4813 break;
4814 }
4815 }
4816 rest = skipwhite(p);
4817 }
4818 else
4819 {
4820 /*
4821 * Allocate room for a syn_pattern, and link it in the list of
4822 * syn_patterns for this item, at the start (because the list is
4823 * used from end to start).
4824 */
4825 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
4826 if (ppp == NULL)
4827 {
4828 rest = NULL;
4829 break;
4830 }
4831 ppp->pp_next = pat_ptrs[item];
4832 pat_ptrs[item] = ppp;
4833 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
4834 if (ppp->pp_synp == NULL)
4835 {
4836 rest = NULL;
4837 break;
4838 }
4839
4840 /*
4841 * Get the syntax pattern and the following offset(s).
4842 */
4843 /* Enable the appropriate \z specials. */
4844 if (item == ITEM_START)
4845 reg_do_extmatch = REX_SET;
4846 else if (item == ITEM_SKIP || item == ITEM_END)
4847 reg_do_extmatch = REX_USE;
4848 rest = get_syn_pattern(rest, ppp->pp_synp);
4849 reg_do_extmatch = 0;
4850 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004851 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004852 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
4853 ppp->pp_matchgroup_id = matchgroup_id;
4854 ++pat_count;
4855 }
4856 }
4857 vim_free(key);
4858 if (illegal || not_enough)
4859 rest = NULL;
4860
4861 /*
4862 * Must have a "start" and "end" pattern.
4863 */
4864 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
4865 pat_ptrs[ITEM_END] == NULL))
4866 {
4867 not_enough = TRUE;
4868 rest = NULL;
4869 }
4870
4871 if (rest != NULL)
4872 {
4873 /*
4874 * Check for trailing garbage or command.
4875 * If OK, add the item.
4876 */
4877 eap->nextcmd = check_nextcmd(rest);
4878 if (!ends_excmd(*rest) || eap->skip)
4879 rest = NULL;
4880 else if (ga_grow(&(curbuf->b_syn_patterns), pat_count) != FAIL
4881 && (syn_id = syn_check_group(arg,
4882 (int)(group_name_end - arg))) != 0)
4883 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004884 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004885 /*
4886 * Store the start/skip/end in the syn_items list
4887 */
4888 idx = curbuf->b_syn_patterns.ga_len;
4889 for (item = ITEM_START; item <= ITEM_END; ++item)
4890 {
4891 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
4892 {
4893 SYN_ITEMS(curbuf)[idx] = *(ppp->pp_synp);
4894 SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4895 SYN_ITEMS(curbuf)[idx].sp_type =
4896 (item == ITEM_START) ? SPTYPE_START :
4897 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004898 SYN_ITEMS(curbuf)[idx].sp_flags |= syn_opt_arg.flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004899 SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4900 SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4901 SYN_ITEMS(curbuf)[idx].sp_syn_match_id =
4902 ppp->pp_matchgroup_id;
4903 if (item == ITEM_START)
4904 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004905 SYN_ITEMS(curbuf)[idx].sp_cont_list =
4906 syn_opt_arg.cont_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004907 SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004908 syn_opt_arg.cont_in_list;
4909 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004910 curbuf->b_syn_containedin = TRUE;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004911 SYN_ITEMS(curbuf)[idx].sp_next_list =
4912 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004913 }
4914 ++curbuf->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004915 ++idx;
4916#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004917 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004918 ++curbuf->b_syn_folditems;
4919#endif
4920 }
4921 }
4922
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004923 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004924 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
4925 success = TRUE; /* don't free the progs and patterns now */
4926 }
4927 }
4928
4929 /*
4930 * Free the allocated memory.
4931 */
4932 for (item = ITEM_START; item <= ITEM_END; ++item)
4933 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
4934 {
4935 if (!success)
4936 {
4937 vim_free(ppp->pp_synp->sp_prog);
4938 vim_free(ppp->pp_synp->sp_pattern);
4939 }
4940 vim_free(ppp->pp_synp);
4941 ppp_next = ppp->pp_next;
4942 vim_free(ppp);
4943 }
4944
4945 if (!success)
4946 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004947 vim_free(syn_opt_arg.cont_list);
4948 vim_free(syn_opt_arg.cont_in_list);
4949 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004950 if (not_enough)
4951 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
4952 else if (illegal || rest == NULL)
4953 EMSG2(_(e_invarg2), arg);
4954 }
4955}
4956
4957/*
4958 * A simple syntax group ID comparison function suitable for use in qsort()
4959 */
4960 static int
4961#ifdef __BORLANDC__
4962_RTLENTRYF
4963#endif
4964syn_compare_stub(v1, v2)
4965 const void *v1;
4966 const void *v2;
4967{
4968 const short *s1 = v1;
4969 const short *s2 = v2;
4970
4971 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
4972}
4973
4974/*
4975 * Combines lists of syntax clusters.
4976 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
4977 */
4978 static void
4979syn_combine_list(clstr1, clstr2, list_op)
4980 short **clstr1;
4981 short **clstr2;
4982 int list_op;
4983{
4984 int count1 = 0;
4985 int count2 = 0;
4986 short *g1;
4987 short *g2;
4988 short *clstr = NULL;
4989 int count;
4990 int round;
4991
4992 /*
4993 * Handle degenerate cases.
4994 */
4995 if (*clstr2 == NULL)
4996 return;
4997 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
4998 {
4999 if (list_op == CLUSTER_REPLACE)
5000 vim_free(*clstr1);
5001 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5002 *clstr1 = *clstr2;
5003 else
5004 vim_free(*clstr2);
5005 return;
5006 }
5007
5008 for (g1 = *clstr1; *g1; g1++)
5009 ++count1;
5010 for (g2 = *clstr2; *g2; g2++)
5011 ++count2;
5012
5013 /*
5014 * For speed purposes, sort both lists.
5015 */
5016 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5017 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5018
5019 /*
5020 * We proceed in two passes; in round 1, we count the elements to place
5021 * in the new list, and in round 2, we allocate and populate the new
5022 * list. For speed, we use a mergesort-like method, adding the smaller
5023 * of the current elements in each list to the new list.
5024 */
5025 for (round = 1; round <= 2; round++)
5026 {
5027 g1 = *clstr1;
5028 g2 = *clstr2;
5029 count = 0;
5030
5031 /*
5032 * First, loop through the lists until one of them is empty.
5033 */
5034 while (*g1 && *g2)
5035 {
5036 /*
5037 * We always want to add from the first list.
5038 */
5039 if (*g1 < *g2)
5040 {
5041 if (round == 2)
5042 clstr[count] = *g1;
5043 count++;
5044 g1++;
5045 continue;
5046 }
5047 /*
5048 * We only want to add from the second list if we're adding the
5049 * lists.
5050 */
5051 if (list_op == CLUSTER_ADD)
5052 {
5053 if (round == 2)
5054 clstr[count] = *g2;
5055 count++;
5056 }
5057 if (*g1 == *g2)
5058 g1++;
5059 g2++;
5060 }
5061
5062 /*
5063 * Now add the leftovers from whichever list didn't get finished
5064 * first. As before, we only want to add from the second list if
5065 * we're adding the lists.
5066 */
5067 for (; *g1; g1++, count++)
5068 if (round == 2)
5069 clstr[count] = *g1;
5070 if (list_op == CLUSTER_ADD)
5071 for (; *g2; g2++, count++)
5072 if (round == 2)
5073 clstr[count] = *g2;
5074
5075 if (round == 1)
5076 {
5077 /*
5078 * If the group ended up empty, we don't need to allocate any
5079 * space for it.
5080 */
5081 if (count == 0)
5082 {
5083 clstr = NULL;
5084 break;
5085 }
5086 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5087 if (clstr == NULL)
5088 break;
5089 clstr[count] = 0;
5090 }
5091 }
5092
5093 /*
5094 * Finally, put the new list in place.
5095 */
5096 vim_free(*clstr1);
5097 vim_free(*clstr2);
5098 *clstr1 = clstr;
5099}
5100
5101/*
5102 * Lookup a syntax cluster name and return it's ID.
5103 * If it is not found, 0 is returned.
5104 */
5105 static int
5106syn_scl_name2id(name)
5107 char_u *name;
5108{
5109 int i;
5110 char_u *name_u;
5111
5112 /* Avoid using stricmp() too much, it's slow on some systems */
5113 name_u = vim_strsave_up(name);
5114 if (name_u == NULL)
5115 return 0;
5116 for (i = curbuf->b_syn_clusters.ga_len; --i >= 0; )
5117 if (SYN_CLSTR(curbuf)[i].scl_name_u != NULL
5118 && STRCMP(name_u, SYN_CLSTR(curbuf)[i].scl_name_u) == 0)
5119 break;
5120 vim_free(name_u);
5121 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5122}
5123
5124/*
5125 * Like syn_scl_name2id(), but take a pointer + length argument.
5126 */
5127 static int
5128syn_scl_namen2id(linep, len)
5129 char_u *linep;
5130 int len;
5131{
5132 char_u *name;
5133 int id = 0;
5134
5135 name = vim_strnsave(linep, len);
5136 if (name != NULL)
5137 {
5138 id = syn_scl_name2id(name);
5139 vim_free(name);
5140 }
5141 return id;
5142}
5143
5144/*
5145 * Find syntax cluster name in the table and return it's ID.
5146 * The argument is a pointer to the name and the length of the name.
5147 * If it doesn't exist yet, a new entry is created.
5148 * Return 0 for failure.
5149 */
5150 static int
5151syn_check_cluster(pp, len)
5152 char_u *pp;
5153 int len;
5154{
5155 int id;
5156 char_u *name;
5157
5158 name = vim_strnsave(pp, len);
5159 if (name == NULL)
5160 return 0;
5161
5162 id = syn_scl_name2id(name);
5163 if (id == 0) /* doesn't exist yet */
5164 id = syn_add_cluster(name);
5165 else
5166 vim_free(name);
5167 return id;
5168}
5169
5170/*
5171 * Add new syntax cluster and return it's ID.
5172 * "name" must be an allocated string, it will be consumed.
5173 * Return 0 for failure.
5174 */
5175 static int
5176syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005177 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005178{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005179 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005180
5181 /*
5182 * First call for this growarray: init growing array.
5183 */
5184 if (curbuf->b_syn_clusters.ga_data == NULL)
5185 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00005186 curbuf->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005187 curbuf->b_syn_clusters.ga_growsize = 10;
5188 }
5189
5190 /*
5191 * Make room for at least one other cluster entry.
5192 */
5193 if (ga_grow(&curbuf->b_syn_clusters, 1) == FAIL)
5194 {
5195 vim_free(name);
5196 return 0;
5197 }
5198 len = curbuf->b_syn_clusters.ga_len;
5199
Bram Moolenaar217ad922005-03-20 22:37:15 +00005200 vim_memset(&(SYN_CLSTR(curbuf)[len]), 0, sizeof(syn_cluster_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005201 SYN_CLSTR(curbuf)[len].scl_name = name;
5202 SYN_CLSTR(curbuf)[len].scl_name_u = vim_strsave_up(name);
5203 SYN_CLSTR(curbuf)[len].scl_list = NULL;
5204 ++curbuf->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005205
Bram Moolenaar217ad922005-03-20 22:37:15 +00005206 if (STRICMP(name, "Spell") == 0)
5207 curbuf->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005208 if (STRICMP(name, "NoSpell") == 0)
5209 curbuf->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005210
Bram Moolenaar071d4272004-06-13 20:20:40 +00005211 return len + SYNID_CLUSTER;
5212}
5213
5214/*
5215 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5216 * [add={groupname},..] [remove={groupname},..]".
5217 */
5218/* ARGSUSED */
5219 static void
5220syn_cmd_cluster(eap, syncing)
5221 exarg_T *eap;
5222 int syncing; /* not used */
5223{
5224 char_u *arg = eap->arg;
5225 char_u *group_name_end;
5226 char_u *rest;
5227 int scl_id;
5228 short *clstr_list;
5229 int got_clstr = FALSE;
5230 int opt_len;
5231 int list_op;
5232
5233 eap->nextcmd = find_nextcmd(arg);
5234 if (eap->skip)
5235 return;
5236
5237 rest = get_group_name(arg, &group_name_end);
5238
5239 if (rest != NULL)
5240 {
5241 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
Bram Moolenaar217ad922005-03-20 22:37:15 +00005242 - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005243
5244 for (;;)
5245 {
5246 if (STRNICMP(rest, "add", 3) == 0
5247 && (vim_iswhite(rest[3]) || rest[3] == '='))
5248 {
5249 opt_len = 3;
5250 list_op = CLUSTER_ADD;
5251 }
5252 else if (STRNICMP(rest, "remove", 6) == 0
5253 && (vim_iswhite(rest[6]) || rest[6] == '='))
5254 {
5255 opt_len = 6;
5256 list_op = CLUSTER_SUBTRACT;
5257 }
5258 else if (STRNICMP(rest, "contains", 8) == 0
5259 && (vim_iswhite(rest[8]) || rest[8] == '='))
5260 {
5261 opt_len = 8;
5262 list_op = CLUSTER_REPLACE;
5263 }
5264 else
5265 break;
5266
5267 clstr_list = NULL;
5268 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5269 {
5270 EMSG2(_(e_invarg2), rest);
5271 break;
5272 }
5273 syn_combine_list(&SYN_CLSTR(curbuf)[scl_id].scl_list,
5274 &clstr_list, list_op);
5275 got_clstr = TRUE;
5276 }
5277
5278 if (got_clstr)
5279 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005280 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005281 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
5282 }
5283 }
5284
5285 if (!got_clstr)
5286 EMSG(_("E400: No cluster specified"));
5287 if (rest == NULL || !ends_excmd(*rest))
5288 EMSG2(_(e_invarg2), arg);
5289}
5290
5291/*
5292 * On first call for current buffer: Init growing array.
5293 */
5294 static void
5295init_syn_patterns()
5296{
5297 curbuf->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5298 curbuf->b_syn_patterns.ga_growsize = 10;
5299}
5300
5301/*
5302 * Get one pattern for a ":syntax match" or ":syntax region" command.
5303 * Stores the pattern and program in a synpat_T.
5304 * Returns a pointer to the next argument, or NULL in case of an error.
5305 */
5306 static char_u *
5307get_syn_pattern(arg, ci)
5308 char_u *arg;
5309 synpat_T *ci;
5310{
5311 char_u *end;
5312 int *p;
5313 int idx;
5314 char_u *cpo_save;
5315
5316 /* need at least three chars */
5317 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5318 return NULL;
5319
5320 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5321 if (*end != *arg) /* end delimiter not found */
5322 {
5323 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5324 return NULL;
5325 }
5326 /* store the pattern and compiled regexp program */
5327 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5328 return NULL;
5329
5330 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5331 cpo_save = p_cpo;
5332 p_cpo = (char_u *)"";
5333 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5334 p_cpo = cpo_save;
5335
5336 if (ci->sp_prog == NULL)
5337 return NULL;
5338 ci->sp_ic = curbuf->b_syn_ic;
5339
5340 /*
5341 * Check for a match, highlight or region offset.
5342 */
5343 ++end;
5344 do
5345 {
5346 for (idx = SPO_COUNT; --idx >= 0; )
5347 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5348 break;
5349 if (idx >= 0)
5350 {
5351 p = &(ci->sp_offsets[idx]);
5352 if (idx != SPO_LC_OFF)
5353 switch (end[3])
5354 {
5355 case 's': break;
5356 case 'b': break;
5357 case 'e': idx += SPO_COUNT; break;
5358 default: idx = -1; break;
5359 }
5360 if (idx >= 0)
5361 {
5362 ci->sp_off_flags |= (1 << idx);
5363 if (idx == SPO_LC_OFF) /* lc=99 */
5364 {
5365 end += 3;
5366 *p = getdigits(&end);
5367
5368 /* "lc=" offset automatically sets "ms=" offset */
5369 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5370 {
5371 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5372 ci->sp_offsets[SPO_MS_OFF] = *p;
5373 }
5374 }
5375 else /* yy=x+99 */
5376 {
5377 end += 4;
5378 if (*end == '+')
5379 {
5380 ++end;
5381 *p = getdigits(&end); /* positive offset */
5382 }
5383 else if (*end == '-')
5384 {
5385 ++end;
5386 *p = -getdigits(&end); /* negative offset */
5387 }
5388 }
5389 if (*end != ',')
5390 break;
5391 ++end;
5392 }
5393 }
5394 } while (idx >= 0);
5395
5396 if (!ends_excmd(*end) && !vim_iswhite(*end))
5397 {
5398 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5399 return NULL;
5400 }
5401 return skipwhite(end);
5402}
5403
5404/*
5405 * Handle ":syntax sync .." command.
5406 */
5407/* ARGSUSED */
5408 static void
5409syn_cmd_sync(eap, syncing)
5410 exarg_T *eap;
5411 int syncing; /* not used */
5412{
5413 char_u *arg_start = eap->arg;
5414 char_u *arg_end;
5415 char_u *key = NULL;
5416 char_u *next_arg;
5417 int illegal = FALSE;
5418 int finished = FALSE;
5419 long n;
5420 char_u *cpo_save;
5421
5422 if (ends_excmd(*arg_start))
5423 {
5424 syn_cmd_list(eap, TRUE);
5425 return;
5426 }
5427
5428 while (!ends_excmd(*arg_start))
5429 {
5430 arg_end = skiptowhite(arg_start);
5431 next_arg = skipwhite(arg_end);
5432 vim_free(key);
5433 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5434 if (STRCMP(key, "CCOMMENT") == 0)
5435 {
5436 if (!eap->skip)
5437 curbuf->b_syn_sync_flags |= SF_CCOMMENT;
5438 if (!ends_excmd(*next_arg))
5439 {
5440 arg_end = skiptowhite(next_arg);
5441 if (!eap->skip)
5442 curbuf->b_syn_sync_id = syn_check_group(next_arg,
5443 (int)(arg_end - next_arg));
5444 next_arg = skipwhite(arg_end);
5445 }
5446 else if (!eap->skip)
5447 curbuf->b_syn_sync_id = syn_name2id((char_u *)"Comment");
5448 }
5449 else if ( STRNCMP(key, "LINES", 5) == 0
5450 || STRNCMP(key, "MINLINES", 8) == 0
5451 || STRNCMP(key, "MAXLINES", 8) == 0
5452 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5453 {
5454 if (key[4] == 'S')
5455 arg_end = key + 6;
5456 else if (key[0] == 'L')
5457 arg_end = key + 11;
5458 else
5459 arg_end = key + 9;
5460 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5461 {
5462 illegal = TRUE;
5463 break;
5464 }
5465 n = getdigits(&arg_end);
5466 if (!eap->skip)
5467 {
5468 if (key[4] == 'B')
5469 curbuf->b_syn_sync_linebreaks = n;
5470 else if (key[1] == 'A')
5471 curbuf->b_syn_sync_maxlines = n;
5472 else
5473 curbuf->b_syn_sync_minlines = n;
5474 }
5475 }
5476 else if (STRCMP(key, "FROMSTART") == 0)
5477 {
5478 if (!eap->skip)
5479 {
5480 curbuf->b_syn_sync_minlines = MAXLNUM;
5481 curbuf->b_syn_sync_maxlines = 0;
5482 }
5483 }
5484 else if (STRCMP(key, "LINECONT") == 0)
5485 {
5486 if (curbuf->b_syn_linecont_pat != NULL)
5487 {
5488 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5489 finished = TRUE;
5490 break;
5491 }
5492 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5493 if (*arg_end != *next_arg) /* end delimiter not found */
5494 {
5495 illegal = TRUE;
5496 break;
5497 }
5498
5499 if (!eap->skip)
5500 {
5501 /* store the pattern and compiled regexp program */
5502 if ((curbuf->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
5503 (int)(arg_end - next_arg - 1))) == NULL)
5504 {
5505 finished = TRUE;
5506 break;
5507 }
5508 curbuf->b_syn_linecont_ic = curbuf->b_syn_ic;
5509
5510 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5511 cpo_save = p_cpo;
5512 p_cpo = (char_u *)"";
5513 curbuf->b_syn_linecont_prog =
5514 vim_regcomp(curbuf->b_syn_linecont_pat, RE_MAGIC);
5515 p_cpo = cpo_save;
5516
5517 if (curbuf->b_syn_linecont_prog == NULL)
5518 {
5519 vim_free(curbuf->b_syn_linecont_pat);
5520 curbuf->b_syn_linecont_pat = NULL;
5521 finished = TRUE;
5522 break;
5523 }
5524 }
5525 next_arg = skipwhite(arg_end + 1);
5526 }
5527 else
5528 {
5529 eap->arg = next_arg;
5530 if (STRCMP(key, "MATCH") == 0)
5531 syn_cmd_match(eap, TRUE);
5532 else if (STRCMP(key, "REGION") == 0)
5533 syn_cmd_region(eap, TRUE);
5534 else if (STRCMP(key, "CLEAR") == 0)
5535 syn_cmd_clear(eap, TRUE);
5536 else
5537 illegal = TRUE;
5538 finished = TRUE;
5539 break;
5540 }
5541 arg_start = next_arg;
5542 }
5543 vim_free(key);
5544 if (illegal)
5545 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5546 else if (!finished)
5547 {
5548 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005549 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005550 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
5551 }
5552}
5553
5554/*
5555 * Convert a line of highlight group names into a list of group ID numbers.
5556 * "arg" should point to the "contains" or "nextgroup" keyword.
5557 * "arg" is advanced to after the last group name.
5558 * Careful: the argument is modified (NULs added).
5559 * returns FAIL for some error, OK for success.
5560 */
5561 static int
5562get_id_list(arg, keylen, list)
5563 char_u **arg;
5564 int keylen; /* length of keyword */
5565 short **list; /* where to store the resulting list, if not
5566 NULL, the list is silently skipped! */
5567{
5568 char_u *p = NULL;
5569 char_u *end;
5570 int round;
5571 int count;
5572 int total_count = 0;
5573 short *retval = NULL;
5574 char_u *name;
5575 regmatch_T regmatch;
5576 int id;
5577 int i;
5578 int failed = FALSE;
5579
5580 /*
5581 * We parse the list twice:
5582 * round == 1: count the number of items, allocate the array.
5583 * round == 2: fill the array with the items.
5584 * In round 1 new groups may be added, causing the number of items to
5585 * grow when a regexp is used. In that case round 1 is done once again.
5586 */
5587 for (round = 1; round <= 2; ++round)
5588 {
5589 /*
5590 * skip "contains"
5591 */
5592 p = skipwhite(*arg + keylen);
5593 if (*p != '=')
5594 {
5595 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5596 break;
5597 }
5598 p = skipwhite(p + 1);
5599 if (ends_excmd(*p))
5600 {
5601 EMSG2(_("E406: Empty argument: %s"), *arg);
5602 break;
5603 }
5604
5605 /*
5606 * parse the arguments after "contains"
5607 */
5608 count = 0;
5609 while (!ends_excmd(*p))
5610 {
5611 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5612 ;
5613 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5614 if (name == NULL)
5615 {
5616 failed = TRUE;
5617 break;
5618 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005619 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005620 if ( STRCMP(name + 1, "ALLBUT") == 0
5621 || STRCMP(name + 1, "ALL") == 0
5622 || STRCMP(name + 1, "TOP") == 0
5623 || STRCMP(name + 1, "CONTAINED") == 0)
5624 {
5625 if (TOUPPER_ASC(**arg) != 'C')
5626 {
5627 EMSG2(_("E407: %s not allowed here"), name + 1);
5628 failed = TRUE;
5629 vim_free(name);
5630 break;
5631 }
5632 if (count != 0)
5633 {
5634 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5635 failed = TRUE;
5636 vim_free(name);
5637 break;
5638 }
5639 if (name[1] == 'A')
5640 id = SYNID_ALLBUT;
5641 else if (name[1] == 'T')
5642 id = SYNID_TOP;
5643 else
5644 id = SYNID_CONTAINED;
5645 id += current_syn_inc_tag;
5646 }
5647 else if (name[1] == '@')
5648 {
5649 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5650 }
5651 else
5652 {
5653 /*
5654 * Handle full group name.
5655 */
5656 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5657 id = syn_check_group(name + 1, (int)(end - p));
5658 else
5659 {
5660 /*
5661 * Handle match of regexp with group names.
5662 */
5663 *name = '^';
5664 STRCAT(name, "$");
5665 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5666 if (regmatch.regprog == NULL)
5667 {
5668 failed = TRUE;
5669 vim_free(name);
5670 break;
5671 }
5672
5673 regmatch.rm_ic = TRUE;
5674 id = 0;
5675 for (i = highlight_ga.ga_len; --i >= 0; )
5676 {
5677 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5678 (colnr_T)0))
5679 {
5680 if (round == 2)
5681 {
5682 /* Got more items than expected; can happen
5683 * when adding items that match:
5684 * "contains=a.*b,axb".
5685 * Go back to first round */
5686 if (count >= total_count)
5687 {
5688 vim_free(retval);
5689 round = 1;
5690 }
5691 else
5692 retval[count] = i + 1;
5693 }
5694 ++count;
5695 id = -1; /* remember that we found one */
5696 }
5697 }
5698 vim_free(regmatch.regprog);
5699 }
5700 }
5701 vim_free(name);
5702 if (id == 0)
5703 {
5704 EMSG2(_("E409: Unknown group name: %s"), p);
5705 failed = TRUE;
5706 break;
5707 }
5708 if (id > 0)
5709 {
5710 if (round == 2)
5711 {
5712 /* Got more items than expected, go back to first round */
5713 if (count >= total_count)
5714 {
5715 vim_free(retval);
5716 round = 1;
5717 }
5718 else
5719 retval[count] = id;
5720 }
5721 ++count;
5722 }
5723 p = skipwhite(end);
5724 if (*p != ',')
5725 break;
5726 p = skipwhite(p + 1); /* skip comma in between arguments */
5727 }
5728 if (failed)
5729 break;
5730 if (round == 1)
5731 {
5732 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5733 if (retval == NULL)
5734 break;
5735 retval[count] = 0; /* zero means end of the list */
5736 total_count = count;
5737 }
5738 }
5739
5740 *arg = p;
5741 if (failed || retval == NULL)
5742 {
5743 vim_free(retval);
5744 return FAIL;
5745 }
5746
5747 if (*list == NULL)
5748 *list = retval;
5749 else
5750 vim_free(retval); /* list already found, don't overwrite it */
5751
5752 return OK;
5753}
5754
5755/*
5756 * Make a copy of an ID list.
5757 */
5758 static short *
5759copy_id_list(list)
5760 short *list;
5761{
5762 int len;
5763 int count;
5764 short *retval;
5765
5766 if (list == NULL)
5767 return NULL;
5768
5769 for (count = 0; list[count]; ++count)
5770 ;
5771 len = (count + 1) * sizeof(short);
5772 retval = (short *)alloc((unsigned)len);
5773 if (retval != NULL)
5774 mch_memmove(retval, list, (size_t)len);
5775
5776 return retval;
5777}
5778
5779/*
5780 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
5781 * "cur_si" can be NULL if not checking the "containedin" list.
5782 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
5783 * the current item.
5784 * This function is called very often, keep it fast!!
5785 */
5786 static int
5787in_id_list(cur_si, list, ssp, contained)
5788 stateitem_T *cur_si; /* current item or NULL */
5789 short *list; /* id list */
5790 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
5791 int contained; /* group id is contained */
5792{
5793 int retval;
5794 short *scl_list;
5795 short item;
5796 short id = ssp->id;
5797 static int depth = 0;
5798 int r;
5799
5800 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00005801 if (cur_si != NULL && ssp->cont_in_list != NULL
5802 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005803 {
5804 /* Ignore transparent items without a contains argument. Double check
5805 * that we don't go back past the first one. */
5806 while ((cur_si->si_flags & HL_TRANS_CONT)
5807 && cur_si > (stateitem_T *)(current_state.ga_data))
5808 --cur_si;
5809 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
5810 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
5811 &(SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_syn),
5812 SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_flags & HL_CONTAINED))
5813 return TRUE;
5814 }
5815
5816 if (list == NULL)
5817 return FALSE;
5818
5819 /*
5820 * If list is ID_LIST_ALL, we are in a transparent item that isn't
5821 * inside anything. Only allow not-contained groups.
5822 */
5823 if (list == ID_LIST_ALL)
5824 return !contained;
5825
5826 /*
5827 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
5828 * contains list. We also require that "id" is at the same ":syn include"
5829 * level as the list.
5830 */
5831 item = *list;
5832 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
5833 {
5834 if (item < SYNID_TOP)
5835 {
5836 /* ALL or ALLBUT: accept all groups in the same file */
5837 if (item - SYNID_ALLBUT != ssp->inc_tag)
5838 return FALSE;
5839 }
5840 else if (item < SYNID_CONTAINED)
5841 {
5842 /* TOP: accept all not-contained groups in the same file */
5843 if (item - SYNID_TOP != ssp->inc_tag || contained)
5844 return FALSE;
5845 }
5846 else
5847 {
5848 /* CONTAINED: accept all contained groups in the same file */
5849 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
5850 return FALSE;
5851 }
5852 item = *++list;
5853 retval = FALSE;
5854 }
5855 else
5856 retval = TRUE;
5857
5858 /*
5859 * Return "retval" if id is in the contains list.
5860 */
5861 while (item != 0)
5862 {
5863 if (item == id)
5864 return retval;
5865 if (item >= SYNID_CLUSTER)
5866 {
5867 scl_list = SYN_CLSTR(syn_buf)[item - SYNID_CLUSTER].scl_list;
5868 /* restrict recursiveness to 30 to avoid an endless loop for a
5869 * cluster that includes itself (indirectly) */
5870 if (scl_list != NULL && depth < 30)
5871 {
5872 ++depth;
5873 r = in_id_list(NULL, scl_list, ssp, contained);
5874 --depth;
5875 if (r)
5876 return retval;
5877 }
5878 }
5879 item = *++list;
5880 }
5881 return !retval;
5882}
5883
5884struct subcommand
5885{
5886 char *name; /* subcommand name */
5887 void (*func)__ARGS((exarg_T *, int)); /* function to call */
5888};
5889
5890static struct subcommand subcommands[] =
5891{
5892 {"case", syn_cmd_case},
5893 {"clear", syn_cmd_clear},
5894 {"cluster", syn_cmd_cluster},
5895 {"enable", syn_cmd_enable},
5896 {"include", syn_cmd_include},
5897 {"keyword", syn_cmd_keyword},
5898 {"list", syn_cmd_list},
5899 {"manual", syn_cmd_manual},
5900 {"match", syn_cmd_match},
5901 {"on", syn_cmd_on},
5902 {"off", syn_cmd_off},
5903 {"region", syn_cmd_region},
5904 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005905 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00005906 {"sync", syn_cmd_sync},
5907 {"", syn_cmd_list},
5908 {NULL, NULL}
5909};
5910
5911/*
5912 * ":syntax".
5913 * This searches the subcommands[] table for the subcommand name, and calls a
5914 * syntax_subcommand() function to do the rest.
5915 */
5916 void
5917ex_syntax(eap)
5918 exarg_T *eap;
5919{
5920 char_u *arg = eap->arg;
5921 char_u *subcmd_end;
5922 char_u *subcmd_name;
5923 int i;
5924
5925 syn_cmdlinep = eap->cmdlinep;
5926
5927 /* isolate subcommand name */
5928 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
5929 ;
5930 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
5931 if (subcmd_name != NULL)
5932 {
5933 if (eap->skip) /* skip error messages for all subcommands */
5934 ++emsg_skip;
5935 for (i = 0; ; ++i)
5936 {
5937 if (subcommands[i].name == NULL)
5938 {
5939 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
5940 break;
5941 }
5942 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
5943 {
5944 eap->arg = skipwhite(subcmd_end);
5945 (subcommands[i].func)(eap, FALSE);
5946 break;
5947 }
5948 }
5949 vim_free(subcmd_name);
5950 if (eap->skip)
5951 --emsg_skip;
5952 }
5953}
5954
5955 int
5956syntax_present(buf)
5957 buf_T *buf;
5958{
5959 return (buf->b_syn_patterns.ga_len != 0
5960 || buf->b_syn_clusters.ga_len != 0
Bram Moolenaardad6b692005-01-25 22:14:34 +00005961 || curbuf->b_keywtab.ht_used > 0
5962 || curbuf->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005963}
5964
5965#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
5966
5967static enum
5968{
5969 EXP_SUBCMD, /* expand ":syn" sub-commands */
5970 EXP_CASE /* expand ":syn case" arguments */
5971} expand_what;
5972
Bram Moolenaar4f688582007-07-24 12:34:30 +00005973/*
5974 * Reset include_link, include_default, include_none to 0.
5975 * Called when we are done expanding.
5976 */
5977 void
5978reset_expand_highlight()
5979{
5980 include_link = include_default = include_none = 0;
5981}
5982
5983/*
5984 * Handle command line completion for :match and :echohl command: Add "None"
5985 * as highlight group.
5986 */
5987 void
5988set_context_in_echohl_cmd(xp, arg)
5989 expand_T *xp;
5990 char_u *arg;
5991{
5992 xp->xp_context = EXPAND_HIGHLIGHT;
5993 xp->xp_pattern = arg;
5994 include_none = 1;
5995}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005996
5997/*
5998 * Handle command line completion for :syntax command.
5999 */
6000 void
6001set_context_in_syntax_cmd(xp, arg)
6002 expand_T *xp;
6003 char_u *arg;
6004{
6005 char_u *p;
6006
6007 /* Default: expand subcommands */
6008 xp->xp_context = EXPAND_SYNTAX;
6009 expand_what = EXP_SUBCMD;
6010 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006011 include_link = 0;
6012 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006013
6014 /* (part of) subcommand already typed */
6015 if (*arg != NUL)
6016 {
6017 p = skiptowhite(arg);
6018 if (*p != NUL) /* past first word */
6019 {
6020 xp->xp_pattern = skipwhite(p);
6021 if (*skiptowhite(xp->xp_pattern) != NUL)
6022 xp->xp_context = EXPAND_NOTHING;
6023 else if (STRNICMP(arg, "case", p - arg) == 0)
6024 expand_what = EXP_CASE;
6025 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6026 || STRNICMP(arg, "region", p - arg) == 0
6027 || STRNICMP(arg, "match", p - arg) == 0
6028 || STRNICMP(arg, "list", p - arg) == 0)
6029 xp->xp_context = EXPAND_HIGHLIGHT;
6030 else
6031 xp->xp_context = EXPAND_NOTHING;
6032 }
6033 }
6034}
6035
6036static char *(case_args[]) = {"match", "ignore", NULL};
6037
6038/*
6039 * Function given to ExpandGeneric() to obtain the list syntax names for
6040 * expansion.
6041 */
6042/*ARGSUSED*/
6043 char_u *
6044get_syntax_name(xp, idx)
6045 expand_T *xp;
6046 int idx;
6047{
6048 if (expand_what == EXP_SUBCMD)
6049 return (char_u *)subcommands[idx].name;
6050 return (char_u *)case_args[idx];
6051}
6052
6053#endif /* FEAT_CMDL_COMPL */
6054
Bram Moolenaar071d4272004-06-13 20:20:40 +00006055/*
6056 * Function called for expression evaluation: get syntax ID at file position.
6057 */
6058 int
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006059syn_get_id(wp, lnum, col, trans, spellp)
6060 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006061 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006062 colnr_T col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006063 int trans; /* remove transparancy */
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006064 int *spellp; /* return: can do spell checking */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006065{
6066 /* When the position is not after the current position and in the same
6067 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006068 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006069 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006070 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006071 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006072
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006073 (void)get_syntax_attr(col, spellp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006074
6075 return (trans ? current_trans_id : current_id);
6076}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006077
6078#if defined(FEAT_FOLDING) || defined(PROTO)
6079/*
6080 * Function called to get folding level for line "lnum" in window "wp".
6081 */
6082 int
6083syn_get_foldlevel(wp, lnum)
6084 win_T *wp;
6085 long lnum;
6086{
6087 int level = 0;
6088 int i;
6089
6090 /* Return quickly when there are no fold items at all. */
6091 if (wp->w_buffer->b_syn_folditems != 0)
6092 {
6093 syntax_start(wp, lnum);
6094
6095 for (i = 0; i < current_state.ga_len; ++i)
6096 if (CUR_STATE(i).si_flags & HL_FOLD)
6097 ++level;
6098 }
6099 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006100 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006101 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006102 if (level < 0)
6103 level = 0;
6104 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006105 return level;
6106}
6107#endif
6108
6109#endif /* FEAT_SYN_HL */
6110
6111
6112/**************************************
6113 * Highlighting stuff *
6114 **************************************/
6115
6116/*
6117 * The default highlight groups. These are compiled-in for fast startup and
6118 * they still work when the runtime files can't be found.
6119 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006120 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6121 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006122 */
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006123#ifdef FEAT_GUI
6124# define CENT(a, b) b
6125#else
6126# define CENT(a, b) a
6127#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006128static char *(highlight_init_both[]) =
6129 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006130 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6131 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
Bram Moolenaarda65f152007-05-06 13:54:35 +00006132#ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006133 CENT("IncSearch term=reverse cterm=reverse",
6134 "IncSearch term=reverse cterm=reverse gui=reverse"),
Bram Moolenaarda65f152007-05-06 13:54:35 +00006135#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006136 CENT("ModeMsg term=bold cterm=bold",
6137 "ModeMsg term=bold cterm=bold gui=bold"),
6138 CENT("NonText term=bold ctermfg=Blue",
6139 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6140 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6141 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6142 CENT("StatusLineNC term=reverse cterm=reverse",
6143 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006144#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006145 CENT("VertSplit term=reverse cterm=reverse",
6146 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006147#endif
6148#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006149 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6150 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006151#endif
6152#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006153 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6154 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006155#endif
6156#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006157 CENT("PmenuThumb cterm=reverse",
6158 "PmenuThumb cterm=reverse gui=reverse"),
6159 CENT("PmenuSbar ctermbg=Grey",
6160 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006161#endif
6162#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006163 CENT("TabLineSel term=bold cterm=bold",
6164 "TabLineSel term=bold cterm=bold gui=bold"),
6165 CENT("TabLineFill term=reverse cterm=reverse",
6166 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006167#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006168#ifdef FEAT_GUI
6169 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006170 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006171#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006172 NULL
6173 };
6174
6175static char *(highlight_init_light[]) =
6176 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006177 CENT("Directory term=bold ctermfg=DarkBlue",
6178 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6179 CENT("LineNr term=underline ctermfg=Brown",
6180 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6181 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6182 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6183 CENT("Question term=standout ctermfg=DarkGreen",
6184 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6185 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6186 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006187#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006188 CENT("SpellBad term=reverse ctermbg=LightRed",
6189 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6190 CENT("SpellCap term=reverse ctermbg=LightBlue",
6191 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6192 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6193 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6194 CENT("SpellLocal term=underline ctermbg=Cyan",
6195 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006196#endif
6197#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006198 CENT("Pmenu ctermbg=LightMagenta",
6199 "Pmenu ctermbg=LightMagenta guibg=LightMagenta"),
6200 CENT("PmenuSel ctermbg=LightGrey",
6201 "PmenuSel ctermbg=LightGrey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006202#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006203 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6204 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6205 CENT("Title term=bold ctermfg=DarkMagenta",
6206 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6207 CENT("WarningMsg term=standout ctermfg=DarkRed",
6208 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006209#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006210 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6211 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006212#endif
6213#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006214 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6215 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6216 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6217 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006218#endif
6219#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006220 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6221 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006222#endif
6223#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006224 CENT("Visual term=reverse",
6225 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006226#endif
6227#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006228 CENT("DiffAdd term=bold ctermbg=LightBlue",
6229 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6230 CENT("DiffChange term=bold ctermbg=LightMagenta",
6231 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6232 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6233 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006234#endif
6235#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006236 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6237 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006238#endif
6239#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006240 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006241 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006242 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006243 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006244#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006245#ifdef FEAT_AUTOCMD
6246 CENT("MatchParen term=reverse ctermbg=Cyan",
6247 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6248#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006249#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006250 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006251#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006252 NULL
6253 };
6254
6255static char *(highlight_init_dark[]) =
6256 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006257 CENT("Directory term=bold ctermfg=LightCyan",
6258 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6259 CENT("LineNr term=underline ctermfg=Yellow",
6260 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6261 CENT("MoreMsg term=bold ctermfg=LightGreen",
6262 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6263 CENT("Question term=standout ctermfg=LightGreen",
6264 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6265 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6266 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6267 CENT("SpecialKey term=bold ctermfg=LightBlue",
6268 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006269#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006270 CENT("SpellBad term=reverse ctermbg=Red",
6271 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6272 CENT("SpellCap term=reverse ctermbg=Blue",
6273 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6274 CENT("SpellRare term=reverse ctermbg=Magenta",
6275 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6276 CENT("SpellLocal term=underline ctermbg=Cyan",
6277 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006278#endif
6279#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006280 CENT("Pmenu ctermbg=Magenta",
6281 "Pmenu ctermbg=Magenta guibg=Magenta"),
6282 CENT("PmenuSel ctermbg=DarkGrey",
6283 "PmenuSel ctermbg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006284#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006285 CENT("Title term=bold ctermfg=LightMagenta",
6286 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6287 CENT("WarningMsg term=standout ctermfg=LightRed",
6288 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006289#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006290 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6291 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006292#endif
6293#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006294 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6295 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6296 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6297 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006298#endif
6299#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006300 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6301 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006302#endif
6303#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006304 CENT("Visual term=reverse",
6305 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006306#endif
6307#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006308 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6309 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6310 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6311 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6312 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6313 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006314#endif
6315#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006316 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6317 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006318#endif
6319#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006320 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006321 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006322 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006323 "CursorLine term=underline cterm=underline guibg=Grey40"),
6324#endif
6325#ifdef FEAT_AUTOCMD
6326 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6327 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006328#endif
6329#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006330 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006331#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006332 NULL
6333 };
6334
6335 void
6336init_highlight(both, reset)
6337 int both; /* include groups where 'bg' doesn't matter */
6338 int reset; /* clear group first */
6339{
6340 int i;
6341 char **pp;
6342 static int had_both = FALSE;
6343#ifdef FEAT_EVAL
6344 char_u *p;
6345
6346 /*
6347 * Try finding the color scheme file. Used when a color file was loaded
6348 * and 'background' or 't_Co' is changed.
6349 */
6350 p = get_var_value((char_u *)"g:colors_name");
6351 if (p != NULL && load_colors(p) == OK)
6352 return;
6353#endif
6354
6355 /*
6356 * Didn't use a color file, use the compiled-in colors.
6357 */
6358 if (both)
6359 {
6360 had_both = TRUE;
6361 pp = highlight_init_both;
6362 for (i = 0; pp[i] != NULL; ++i)
6363 do_highlight((char_u *)pp[i], reset, TRUE);
6364 }
6365 else if (!had_both)
6366 /* Don't do anything before the call with both == TRUE from main().
6367 * Not everything has been setup then, and that call will overrule
6368 * everything anyway. */
6369 return;
6370
6371 if (*p_bg == 'l')
6372 pp = highlight_init_light;
6373 else
6374 pp = highlight_init_dark;
6375 for (i = 0; pp[i] != NULL; ++i)
6376 do_highlight((char_u *)pp[i], reset, TRUE);
6377
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006378 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006379 * depend on the number of colors available.
6380 * With 8 colors brown is equal to yellow, need to use black for Search fg
6381 * to avoid Statement highlighted text disappears. */
Bram Moolenaarab194812005-09-14 21:40:12 +00006382 if (t_colors > 8)
6383 do_highlight((char_u *)(*p_bg == 'l' ? "Visual ctermbg=LightGrey"
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00006384 : "Visual ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006385 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006386 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00006387 do_highlight((char_u *)"Visual cterm=reverse", FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006388 if (*p_bg == 'l')
6389 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
6390 }
Bram Moolenaarab194812005-09-14 21:40:12 +00006391
Bram Moolenaar071d4272004-06-13 20:20:40 +00006392#ifdef FEAT_SYN_HL
6393 /*
6394 * If syntax highlighting is enabled load the highlighting for it.
6395 */
6396 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006397 {
6398 static int recursive = 0;
6399
6400 if (recursive >= 5)
6401 EMSG(_("E679: recursive loop loading syncolor.vim"));
6402 else
6403 {
6404 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006405 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006406 --recursive;
6407 }
6408 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006409#endif
6410}
6411
6412/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006413 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006414 * Return OK for success, FAIL for failure.
6415 */
6416 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006417load_colors(name)
6418 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006419{
6420 char_u *buf;
6421 int retval = FAIL;
6422 static int recursive = FALSE;
6423
6424 /* When being called recursively, this is probably because setting
6425 * 'background' caused the highlighting to be reloaded. This means it is
6426 * working, thus we should return OK. */
6427 if (recursive)
6428 return OK;
6429
6430 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006431 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006432 if (buf != NULL)
6433 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006434 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006435 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006436 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006437#ifdef FEAT_AUTOCMD
6438 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6439#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006440 }
6441 recursive = FALSE;
6442
6443 return retval;
6444}
6445
6446/*
6447 * Handle the ":highlight .." command.
6448 * When using ":hi clear" this is called recursively for each group with
6449 * "forceit" and "init" both TRUE.
6450 */
6451 void
6452do_highlight(line, forceit, init)
6453 char_u *line;
6454 int forceit;
6455 int init; /* TRUE when called for initializing */
6456{
6457 char_u *name_end;
6458 char_u *p;
6459 char_u *linep;
6460 char_u *key_start;
6461 char_u *arg_start;
6462 char_u *key = NULL, *arg = NULL;
6463 long i;
6464 int off;
6465 int len;
6466 int attr;
6467 int id;
6468 int idx;
6469 int dodefault = FALSE;
6470 int doclear = FALSE;
6471 int dolink = FALSE;
6472 int error = FALSE;
6473 int color;
6474 int is_normal_group = FALSE; /* "Normal" group */
6475#ifdef FEAT_GUI_X11
6476 int is_menu_group = FALSE; /* "Menu" group */
6477 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6478 int is_tooltip_group = FALSE; /* "Tooltip" group */
6479 int do_colors = FALSE; /* need to update colors? */
6480#else
6481# define is_menu_group 0
6482# define is_tooltip_group 0
6483#endif
6484
6485 /*
6486 * If no argument, list current highlighting.
6487 */
6488 if (ends_excmd(*line))
6489 {
6490 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6491 /* TODO: only call when the group has attributes set */
6492 highlight_list_one((int)i);
6493 return;
6494 }
6495
6496 /*
6497 * Isolate the name.
6498 */
6499 name_end = skiptowhite(line);
6500 linep = skipwhite(name_end);
6501
6502 /*
6503 * Check for "default" argument.
6504 */
6505 if (STRNCMP(line, "default", name_end - line) == 0)
6506 {
6507 dodefault = TRUE;
6508 line = linep;
6509 name_end = skiptowhite(line);
6510 linep = skipwhite(name_end);
6511 }
6512
6513 /*
6514 * Check for "clear" or "link" argument.
6515 */
6516 if (STRNCMP(line, "clear", name_end - line) == 0)
6517 doclear = TRUE;
6518 if (STRNCMP(line, "link", name_end - line) == 0)
6519 dolink = TRUE;
6520
6521 /*
6522 * ":highlight {group-name}": list highlighting for one group.
6523 */
6524 if (!doclear && !dolink && ends_excmd(*linep))
6525 {
6526 id = syn_namen2id(line, (int)(name_end - line));
6527 if (id == 0)
6528 EMSG2(_("E411: highlight group not found: %s"), line);
6529 else
6530 highlight_list_one(id);
6531 return;
6532 }
6533
6534 /*
6535 * Handle ":highlight link {from} {to}" command.
6536 */
6537 if (dolink)
6538 {
6539 char_u *from_start = linep;
6540 char_u *from_end;
6541 char_u *to_start;
6542 char_u *to_end;
6543 int from_id;
6544 int to_id;
6545
6546 from_end = skiptowhite(from_start);
6547 to_start = skipwhite(from_end);
6548 to_end = skiptowhite(to_start);
6549
6550 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6551 {
6552 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6553 from_start);
6554 return;
6555 }
6556
6557 if (!ends_excmd(*skipwhite(to_end)))
6558 {
6559 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6560 return;
6561 }
6562
6563 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6564 if (STRNCMP(to_start, "NONE", 4) == 0)
6565 to_id = 0;
6566 else
6567 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6568
6569 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6570 {
6571 /*
6572 * Don't allow a link when there already is some highlighting
6573 * for the group, unless '!' is used
6574 */
6575 if (to_id > 0 && !forceit && !init
6576 && hl_has_settings(from_id - 1, dodefault))
6577 {
6578 if (sourcing_name == NULL && !dodefault)
6579 EMSG(_("E414: group has settings, highlight link ignored"));
6580 }
6581 else
6582 {
6583 if (!init)
6584 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6585 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00006586#ifdef FEAT_EVAL
6587 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6588#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00006589 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006590 }
6591 }
6592
6593 /* Only call highlight_changed() once, after sourcing a syntax file */
6594 need_highlight_changed = TRUE;
6595
6596 return;
6597 }
6598
6599 if (doclear)
6600 {
6601 /*
6602 * ":highlight clear [group]" command.
6603 */
6604 line = linep;
6605 if (ends_excmd(*line))
6606 {
6607#ifdef FEAT_GUI
6608 /* First, we do not destroy the old values, but allocate the new
6609 * ones and update the display. THEN we destroy the old values.
6610 * If we destroy the old values first, then the old values
6611 * (such as GuiFont's or GuiFontset's) will still be displayed but
6612 * invalid because they were free'd.
6613 */
6614 if (gui.in_use)
6615 {
6616# ifdef FEAT_BEVAL_TIP
6617 gui_init_tooltip_font();
6618# endif
6619# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6620 gui_init_menu_font();
6621# endif
6622 }
6623# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6624 gui_mch_def_colors();
6625# endif
6626# ifdef FEAT_GUI_X11
6627# ifdef FEAT_MENU
6628
6629 /* This only needs to be done when there is no Menu highlight
6630 * group defined by default, which IS currently the case.
6631 */
6632 gui_mch_new_menu_colors();
6633# endif
6634 if (gui.in_use)
6635 {
6636 gui_new_scrollbar_colors();
6637# ifdef FEAT_BEVAL
6638 gui_mch_new_tooltip_colors();
6639# endif
6640# ifdef FEAT_MENU
6641 gui_mch_new_menu_font();
6642# endif
6643 }
6644# endif
6645
6646 /* Ok, we're done allocating the new default graphics items.
6647 * The screen should already be refreshed at this point.
6648 * It is now Ok to clear out the old data.
6649 */
6650#endif
6651#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00006652 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006653#endif
6654 restore_cterm_colors();
6655
6656 /*
6657 * Clear all default highlight groups and load the defaults.
6658 */
6659 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6660 highlight_clear(idx);
6661 init_highlight(TRUE, TRUE);
6662#ifdef FEAT_GUI
6663 if (gui.in_use)
6664 highlight_gui_started();
6665#endif
6666 highlight_changed();
6667 redraw_later_clear();
6668 return;
6669 }
6670 name_end = skiptowhite(line);
6671 linep = skipwhite(name_end);
6672 }
6673
6674 /*
6675 * Find the group name in the table. If it does not exist yet, add it.
6676 */
6677 id = syn_check_group(line, (int)(name_end - line));
6678 if (id == 0) /* failed (out of memory) */
6679 return;
6680 idx = id - 1; /* index is ID minus one */
6681
6682 /* Return if "default" was used and the group already has settings. */
6683 if (dodefault && hl_has_settings(idx, TRUE))
6684 return;
6685
6686 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
6687 is_normal_group = TRUE;
6688#ifdef FEAT_GUI_X11
6689 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
6690 is_menu_group = TRUE;
6691 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
6692 is_scrollbar_group = TRUE;
6693 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
6694 is_tooltip_group = TRUE;
6695#endif
6696
6697 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
6698 if (doclear || (forceit && init))
6699 {
6700 highlight_clear(idx);
6701 if (!doclear)
6702 HL_TABLE()[idx].sg_set = 0;
6703 }
6704
6705 if (!doclear)
6706 while (!ends_excmd(*linep))
6707 {
6708 key_start = linep;
6709 if (*linep == '=')
6710 {
6711 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
6712 error = TRUE;
6713 break;
6714 }
6715
6716 /*
6717 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
6718 * "guibg").
6719 */
6720 while (*linep && !vim_iswhite(*linep) && *linep != '=')
6721 ++linep;
6722 vim_free(key);
6723 key = vim_strnsave_up(key_start, (int)(linep - key_start));
6724 if (key == NULL)
6725 {
6726 error = TRUE;
6727 break;
6728 }
6729 linep = skipwhite(linep);
6730
6731 if (STRCMP(key, "NONE") == 0)
6732 {
6733 if (!init || HL_TABLE()[idx].sg_set == 0)
6734 {
6735 if (!init)
6736 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
6737 highlight_clear(idx);
6738 }
6739 continue;
6740 }
6741
6742 /*
6743 * Check for the equal sign.
6744 */
6745 if (*linep != '=')
6746 {
6747 EMSG2(_("E416: missing equal sign: %s"), key_start);
6748 error = TRUE;
6749 break;
6750 }
6751 ++linep;
6752
6753 /*
6754 * Isolate the argument.
6755 */
6756 linep = skipwhite(linep);
6757 if (*linep == '\'') /* guifg='color name' */
6758 {
6759 arg_start = ++linep;
6760 linep = vim_strchr(linep, '\'');
6761 if (linep == NULL)
6762 {
6763 EMSG2(_(e_invarg2), key_start);
6764 error = TRUE;
6765 break;
6766 }
6767 }
6768 else
6769 {
6770 arg_start = linep;
6771 linep = skiptowhite(linep);
6772 }
6773 if (linep == arg_start)
6774 {
6775 EMSG2(_("E417: missing argument: %s"), key_start);
6776 error = TRUE;
6777 break;
6778 }
6779 vim_free(arg);
6780 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
6781 if (arg == NULL)
6782 {
6783 error = TRUE;
6784 break;
6785 }
6786 if (*linep == '\'')
6787 ++linep;
6788
6789 /*
6790 * Store the argument.
6791 */
6792 if ( STRCMP(key, "TERM") == 0
6793 || STRCMP(key, "CTERM") == 0
6794 || STRCMP(key, "GUI") == 0)
6795 {
6796 attr = 0;
6797 off = 0;
6798 while (arg[off] != NUL)
6799 {
6800 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
6801 {
6802 len = (int)STRLEN(hl_name_table[i]);
6803 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
6804 {
6805 attr |= hl_attr_table[i];
6806 off += len;
6807 break;
6808 }
6809 }
6810 if (i < 0)
6811 {
6812 EMSG2(_("E418: Illegal value: %s"), arg);
6813 error = TRUE;
6814 break;
6815 }
6816 if (arg[off] == ',') /* another one follows */
6817 ++off;
6818 }
6819 if (error)
6820 break;
6821 if (*key == 'T')
6822 {
6823 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
6824 {
6825 if (!init)
6826 HL_TABLE()[idx].sg_set |= SG_TERM;
6827 HL_TABLE()[idx].sg_term = attr;
6828 }
6829 }
6830 else if (*key == 'C')
6831 {
6832 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6833 {
6834 if (!init)
6835 HL_TABLE()[idx].sg_set |= SG_CTERM;
6836 HL_TABLE()[idx].sg_cterm = attr;
6837 HL_TABLE()[idx].sg_cterm_bold = FALSE;
6838 }
6839 }
6840#ifdef FEAT_GUI
6841 else
6842 {
6843 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6844 {
6845 if (!init)
6846 HL_TABLE()[idx].sg_set |= SG_GUI;
6847 HL_TABLE()[idx].sg_gui = attr;
6848 }
6849 }
6850#endif
6851 }
6852 else if (STRCMP(key, "FONT") == 0)
6853 {
6854 /* in non-GUI fonts are simply ignored */
6855#ifdef FEAT_GUI
6856 if (!gui.shell_created)
6857 {
6858 /* GUI not started yet, always accept the name. */
6859 vim_free(HL_TABLE()[idx].sg_font_name);
6860 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6861 }
6862 else
6863 {
6864 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
6865# ifdef FEAT_XFONTSET
6866 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
6867# endif
6868 /* First, save the current font/fontset.
6869 * Then try to allocate the font/fontset.
6870 * If the allocation fails, HL_TABLE()[idx].sg_font OR
6871 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
6872 */
6873
6874 HL_TABLE()[idx].sg_font = NOFONT;
6875# ifdef FEAT_XFONTSET
6876 HL_TABLE()[idx].sg_fontset = NOFONTSET;
6877# endif
6878 hl_do_font(idx, arg, is_normal_group, is_menu_group,
6879 is_tooltip_group);
6880
6881# ifdef FEAT_XFONTSET
6882 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
6883 {
6884 /* New fontset was accepted. Free the old one, if there was
6885 * one.
6886 */
6887 gui_mch_free_fontset(temp_sg_fontset);
6888 vim_free(HL_TABLE()[idx].sg_font_name);
6889 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6890 }
6891 else
6892 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
6893# endif
6894 if (HL_TABLE()[idx].sg_font != NOFONT)
6895 {
6896 /* New font was accepted. Free the old one, if there was
6897 * one.
6898 */
6899 gui_mch_free_font(temp_sg_font);
6900 vim_free(HL_TABLE()[idx].sg_font_name);
6901 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6902 }
6903 else
6904 HL_TABLE()[idx].sg_font = temp_sg_font;
6905 }
6906#endif
6907 }
6908 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
6909 {
6910 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6911 {
6912 if (!init)
6913 HL_TABLE()[idx].sg_set |= SG_CTERM;
6914
6915 /* When setting the foreground color, and previously the "bold"
6916 * flag was set for a light color, reset it now */
6917 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
6918 {
6919 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6920 HL_TABLE()[idx].sg_cterm_bold = FALSE;
6921 }
6922
6923 if (VIM_ISDIGIT(*arg))
6924 color = atoi((char *)arg);
6925 else if (STRICMP(arg, "fg") == 0)
6926 {
6927 if (cterm_normal_fg_color)
6928 color = cterm_normal_fg_color - 1;
6929 else
6930 {
6931 EMSG(_("E419: FG color unknown"));
6932 error = TRUE;
6933 break;
6934 }
6935 }
6936 else if (STRICMP(arg, "bg") == 0)
6937 {
6938 if (cterm_normal_bg_color > 0)
6939 color = cterm_normal_bg_color - 1;
6940 else
6941 {
6942 EMSG(_("E420: BG color unknown"));
6943 error = TRUE;
6944 break;
6945 }
6946 }
6947 else
6948 {
6949 static char *(color_names[28]) = {
6950 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
6951 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
6952 "Gray", "Grey",
6953 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
6954 "Blue", "LightBlue", "Green", "LightGreen",
6955 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
6956 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
6957 static int color_numbers_16[28] = {0, 1, 2, 3,
6958 4, 5, 6, 6,
6959 7, 7,
6960 7, 7, 8, 8,
6961 9, 9, 10, 10,
6962 11, 11, 12, 12, 13,
6963 13, 14, 14, 15, -1};
6964 /* for xterm with 88 colors... */
6965 static int color_numbers_88[28] = {0, 4, 2, 6,
6966 1, 5, 32, 72,
6967 84, 84,
6968 7, 7, 82, 82,
6969 12, 43, 10, 61,
6970 14, 63, 9, 74, 13,
6971 75, 11, 78, 15, -1};
6972 /* for xterm with 256 colors... */
6973 static int color_numbers_256[28] = {0, 4, 2, 6,
6974 1, 5, 130, 130,
6975 248, 248,
6976 7, 7, 242, 242,
6977 12, 81, 10, 121,
6978 14, 159, 9, 224, 13,
6979 225, 11, 229, 15, -1};
6980 /* for terminals with less than 16 colors... */
6981 static int color_numbers_8[28] = {0, 4, 2, 6,
6982 1, 5, 3, 3,
6983 7, 7,
6984 7, 7, 0+8, 0+8,
6985 4+8, 4+8, 2+8, 2+8,
6986 6+8, 6+8, 1+8, 1+8, 5+8,
6987 5+8, 3+8, 3+8, 7+8, -1};
6988#if defined(__QNXNTO__)
6989 static int *color_numbers_8_qansi = color_numbers_8;
6990 /* On qnx, the 8 & 16 color arrays are the same */
6991 if (STRNCMP(T_NAME, "qansi", 5) == 0)
6992 color_numbers_8_qansi = color_numbers_16;
6993#endif
6994
6995 /* reduce calls to STRICMP a bit, it can be slow */
6996 off = TOUPPER_ASC(*arg);
6997 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
6998 if (off == color_names[i][0]
6999 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7000 break;
7001 if (i < 0)
7002 {
7003 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7004 error = TRUE;
7005 break;
7006 }
7007
7008 /* Use the _16 table to check if its a valid color name. */
7009 color = color_numbers_16[i];
7010 if (color >= 0)
7011 {
7012 if (t_colors == 8)
7013 {
7014 /* t_Co is 8: use the 8 colors table */
7015#if defined(__QNXNTO__)
7016 color = color_numbers_8_qansi[i];
7017#else
7018 color = color_numbers_8[i];
7019#endif
7020 if (key[5] == 'F')
7021 {
7022 /* set/reset bold attribute to get light foreground
7023 * colors (on some terminals, e.g. "linux") */
7024 if (color & 8)
7025 {
7026 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7027 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7028 }
7029 else
7030 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7031 }
7032 color &= 7; /* truncate to 8 colors */
7033 }
7034 else if (t_colors == 16 || t_colors == 88
7035 || t_colors == 256)
7036 {
7037 /*
7038 * Guess: if the termcap entry ends in 'm', it is
7039 * probably an xterm-like terminal. Use the changed
7040 * order for colors.
7041 */
7042 if (*T_CAF != NUL)
7043 p = T_CAF;
7044 else
7045 p = T_CSF;
7046 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7047 switch (t_colors)
7048 {
7049 case 16:
7050 color = color_numbers_8[i];
7051 break;
7052 case 88:
7053 color = color_numbers_88[i];
7054 break;
7055 case 256:
7056 color = color_numbers_256[i];
7057 break;
7058 }
7059 }
7060 }
7061 }
7062 /* Add one to the argument, to avoid zero */
7063 if (key[5] == 'F')
7064 {
7065 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7066 if (is_normal_group)
7067 {
7068 cterm_normal_fg_color = color + 1;
7069 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7070#ifdef FEAT_GUI
7071 /* Don't do this if the GUI is used. */
7072 if (!gui.in_use && !gui.starting)
7073#endif
7074 {
7075 must_redraw = CLEAR;
7076 if (termcap_active)
7077 term_fg_color(color);
7078 }
7079 }
7080 }
7081 else
7082 {
7083 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7084 if (is_normal_group)
7085 {
7086 cterm_normal_bg_color = color + 1;
7087#ifdef FEAT_GUI
7088 /* Don't mess with 'background' if the GUI is used. */
7089 if (!gui.in_use && !gui.starting)
7090#endif
7091 {
7092 must_redraw = CLEAR;
7093 if (termcap_active)
7094 term_bg_color(color);
7095 if (t_colors < 16)
7096 i = (color == 0 || color == 4);
7097 else
7098 i = (color < 7 || color == 8);
7099 /* Set the 'background' option if the value is wrong. */
7100 if (i != (*p_bg == 'd'))
7101 set_option_value((char_u *)"bg", 0L,
7102 i ? (char_u *)"dark" : (char_u *)"light", 0);
7103 }
7104 }
7105 }
7106 }
7107 }
7108 else if (STRCMP(key, "GUIFG") == 0)
7109 {
7110#ifdef FEAT_GUI /* in non-GUI guifg colors are simply ignored */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007111 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007112 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007113 if (!init)
7114 HL_TABLE()[idx].sg_set |= SG_GUI;
7115
7116 i = color_name2handle(arg);
7117 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7118 {
7119 HL_TABLE()[idx].sg_gui_fg = i;
7120 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7121 if (STRCMP(arg, "NONE"))
7122 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7123 else
7124 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007125# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007126 if (is_menu_group)
7127 gui.menu_fg_pixel = i;
7128 if (is_scrollbar_group)
7129 gui.scroll_fg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007130# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007131 if (is_tooltip_group)
7132 gui.tooltip_fg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007133# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007134 do_colors = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007135# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007136 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007137 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007138#endif
7139 }
7140 else if (STRCMP(key, "GUIBG") == 0)
7141 {
7142#ifdef FEAT_GUI /* in non-GUI guibg colors are simply ignored */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007143 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007144 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007145 if (!init)
7146 HL_TABLE()[idx].sg_set |= SG_GUI;
7147
7148 i = color_name2handle(arg);
7149 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7150 {
7151 HL_TABLE()[idx].sg_gui_bg = i;
7152 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7153 if (STRCMP(arg, "NONE") != 0)
7154 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7155 else
7156 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007157# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007158 if (is_menu_group)
7159 gui.menu_bg_pixel = i;
7160 if (is_scrollbar_group)
7161 gui.scroll_bg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007162# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007163 if (is_tooltip_group)
7164 gui.tooltip_bg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007165# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007166 do_colors = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007167# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007168 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007169 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007170#endif
7171 }
7172 else if (STRCMP(key, "GUISP") == 0)
7173 {
7174#ifdef FEAT_GUI /* in non-GUI guisp colors are simply ignored */
7175 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7176 {
7177 if (!init)
7178 HL_TABLE()[idx].sg_set |= SG_GUI;
7179
7180 i = color_name2handle(arg);
7181 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7182 {
7183 HL_TABLE()[idx].sg_gui_sp = i;
7184 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7185 if (STRCMP(arg, "NONE") != 0)
7186 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7187 else
7188 HL_TABLE()[idx].sg_gui_sp_name = NULL;
7189 }
7190 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007191#endif
7192 }
7193 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7194 {
7195 char_u buf[100];
7196 char_u *tname;
7197
7198 if (!init)
7199 HL_TABLE()[idx].sg_set |= SG_TERM;
7200
7201 /*
7202 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007203 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007204 */
7205 if (STRNCMP(arg, "t_", 2) == 0)
7206 {
7207 off = 0;
7208 buf[0] = 0;
7209 while (arg[off] != NUL)
7210 {
7211 /* Isolate one termcap name */
7212 for (len = 0; arg[off + len] &&
7213 arg[off + len] != ','; ++len)
7214 ;
7215 tname = vim_strnsave(arg + off, len);
7216 if (tname == NULL) /* out of memory */
7217 {
7218 error = TRUE;
7219 break;
7220 }
7221 /* lookup the escape sequence for the item */
7222 p = get_term_code(tname);
7223 vim_free(tname);
7224 if (p == NULL) /* ignore non-existing things */
7225 p = (char_u *)"";
7226
7227 /* Append it to the already found stuff */
7228 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7229 {
7230 EMSG2(_("E422: terminal code too long: %s"), arg);
7231 error = TRUE;
7232 break;
7233 }
7234 STRCAT(buf, p);
7235
7236 /* Advance to the next item */
7237 off += len;
7238 if (arg[off] == ',') /* another one follows */
7239 ++off;
7240 }
7241 }
7242 else
7243 {
7244 /*
7245 * Copy characters from arg[] to buf[], translating <> codes.
7246 */
7247 for (p = arg, off = 0; off < 100 && *p; )
7248 {
7249 len = trans_special(&p, buf + off, FALSE);
7250 if (len) /* recognized special char */
7251 off += len;
7252 else /* copy as normal char */
7253 buf[off++] = *p++;
7254 }
7255 buf[off] = NUL;
7256 }
7257 if (error)
7258 break;
7259
7260 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7261 p = NULL;
7262 else
7263 p = vim_strsave(buf);
7264 if (key[2] == 'A')
7265 {
7266 vim_free(HL_TABLE()[idx].sg_start);
7267 HL_TABLE()[idx].sg_start = p;
7268 }
7269 else
7270 {
7271 vim_free(HL_TABLE()[idx].sg_stop);
7272 HL_TABLE()[idx].sg_stop = p;
7273 }
7274 }
7275 else
7276 {
7277 EMSG2(_("E423: Illegal argument: %s"), key_start);
7278 error = TRUE;
7279 break;
7280 }
7281
7282 /*
7283 * When highlighting has been given for a group, don't link it.
7284 */
7285 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7286 HL_TABLE()[idx].sg_link = 0;
7287
7288 /*
7289 * Continue with next argument.
7290 */
7291 linep = skipwhite(linep);
7292 }
7293
7294 /*
7295 * If there is an error, and it's a new entry, remove it from the table.
7296 */
7297 if (error && idx == highlight_ga.ga_len)
7298 syn_unadd_group();
7299 else
7300 {
7301 if (is_normal_group)
7302 {
7303 HL_TABLE()[idx].sg_term_attr = 0;
7304 HL_TABLE()[idx].sg_cterm_attr = 0;
7305#ifdef FEAT_GUI
7306 HL_TABLE()[idx].sg_gui_attr = 0;
7307 /*
7308 * Need to update all groups, because they might be using "bg"
7309 * and/or "fg", which have been changed now.
7310 */
7311 if (gui.in_use)
7312 highlight_gui_started();
7313#endif
7314 }
7315#ifdef FEAT_GUI_X11
7316# ifdef FEAT_MENU
7317 else if (is_menu_group)
7318 {
7319 if (gui.in_use && do_colors)
7320 gui_mch_new_menu_colors();
7321 }
7322# endif
7323 else if (is_scrollbar_group)
7324 {
7325 if (gui.in_use && do_colors)
7326 gui_new_scrollbar_colors();
7327 }
7328# ifdef FEAT_BEVAL
7329 else if (is_tooltip_group)
7330 {
7331 if (gui.in_use && do_colors)
7332 gui_mch_new_tooltip_colors();
7333 }
7334# endif
7335#endif
7336 else
7337 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007338#ifdef FEAT_EVAL
7339 HL_TABLE()[idx].sg_scriptID = current_SID;
7340#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007341 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007342 }
7343 vim_free(key);
7344 vim_free(arg);
7345
7346 /* Only call highlight_changed() once, after sourcing a syntax file */
7347 need_highlight_changed = TRUE;
7348}
7349
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007350#if defined(EXITFREE) || defined(PROTO)
7351 void
7352free_highlight()
7353{
7354 int i;
7355
7356 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007357 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007358 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007359 vim_free(HL_TABLE()[i].sg_name);
7360 vim_free(HL_TABLE()[i].sg_name_u);
7361 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007362 ga_clear(&highlight_ga);
7363}
7364#endif
7365
Bram Moolenaar071d4272004-06-13 20:20:40 +00007366/*
7367 * Reset the cterm colors to what they were before Vim was started, if
7368 * possible. Otherwise reset them to zero.
7369 */
7370 void
7371restore_cterm_colors()
7372{
7373#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7374 /* Since t_me has been set, this probably means that the user
7375 * wants to use this as default colors. Need to reset default
7376 * background/foreground colors. */
7377 mch_set_normal_colors();
7378#else
7379 cterm_normal_fg_color = 0;
7380 cterm_normal_fg_bold = 0;
7381 cterm_normal_bg_color = 0;
7382#endif
7383}
7384
7385/*
7386 * Return TRUE if highlight group "idx" has any settings.
7387 * When "check_link" is TRUE also check for an existing link.
7388 */
7389 static int
7390hl_has_settings(idx, check_link)
7391 int idx;
7392 int check_link;
7393{
7394 return ( HL_TABLE()[idx].sg_term_attr != 0
7395 || HL_TABLE()[idx].sg_cterm_attr != 0
7396#ifdef FEAT_GUI
7397 || HL_TABLE()[idx].sg_gui_attr != 0
7398#endif
7399 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7400}
7401
7402/*
7403 * Clear highlighting for one group.
7404 */
7405 static void
7406highlight_clear(idx)
7407 int idx;
7408{
7409 HL_TABLE()[idx].sg_term = 0;
7410 vim_free(HL_TABLE()[idx].sg_start);
7411 HL_TABLE()[idx].sg_start = NULL;
7412 vim_free(HL_TABLE()[idx].sg_stop);
7413 HL_TABLE()[idx].sg_stop = NULL;
7414 HL_TABLE()[idx].sg_term_attr = 0;
7415 HL_TABLE()[idx].sg_cterm = 0;
7416 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7417 HL_TABLE()[idx].sg_cterm_fg = 0;
7418 HL_TABLE()[idx].sg_cterm_bg = 0;
7419 HL_TABLE()[idx].sg_cterm_attr = 0;
7420#ifdef FEAT_GUI /* in non-GUI fonts are simply ignored */
7421 HL_TABLE()[idx].sg_gui = 0;
7422 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7423 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7424 HL_TABLE()[idx].sg_gui_fg_name = NULL;
7425 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7426 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7427 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007428 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
7429 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7430 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007431 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7432 HL_TABLE()[idx].sg_font = NOFONT;
7433# ifdef FEAT_XFONTSET
7434 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7435 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7436# endif
7437 vim_free(HL_TABLE()[idx].sg_font_name);
7438 HL_TABLE()[idx].sg_font_name = NULL;
7439 HL_TABLE()[idx].sg_gui_attr = 0;
7440#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00007441#ifdef FEAT_EVAL
7442 /* Clear the script ID only when there is no link, since that is not
7443 * cleared. */
7444 if (HL_TABLE()[idx].sg_link == 0)
7445 HL_TABLE()[idx].sg_scriptID = 0;
7446#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007447}
7448
7449#if defined(FEAT_GUI) || defined(PROTO)
7450/*
7451 * Set the normal foreground and background colors according to the "Normal"
7452 * highlighighting group. For X11 also set "Menu", "Scrollbar", and
7453 * "Tooltip" colors.
7454 */
7455 void
7456set_normal_colors()
7457{
7458 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007459 &gui.norm_pixel, &gui.back_pixel,
7460 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007461 {
7462 gui_mch_new_colors();
7463 must_redraw = CLEAR;
7464 }
7465#ifdef FEAT_GUI_X11
7466 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007467 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7468 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007469 {
7470# ifdef FEAT_MENU
7471 gui_mch_new_menu_colors();
7472# endif
7473 must_redraw = CLEAR;
7474 }
7475# ifdef FEAT_BEVAL
7476 if (set_group_colors((char_u *)"Tooltip",
7477 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7478 FALSE, FALSE, TRUE))
7479 {
7480# ifdef FEAT_TOOLBAR
7481 gui_mch_new_tooltip_colors();
7482# endif
7483 must_redraw = CLEAR;
7484 }
7485#endif
7486 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007487 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7488 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007489 {
7490 gui_new_scrollbar_colors();
7491 must_redraw = CLEAR;
7492 }
7493#endif
7494}
7495
7496/*
7497 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7498 */
7499 static int
7500set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7501 char_u *name;
7502 guicolor_T *fgp;
7503 guicolor_T *bgp;
7504 int do_menu;
7505 int use_norm;
7506 int do_tooltip;
7507{
7508 int idx;
7509
7510 idx = syn_name2id(name) - 1;
7511 if (idx >= 0)
7512 {
7513 gui_do_one_color(idx, do_menu, do_tooltip);
7514
7515 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7516 *fgp = HL_TABLE()[idx].sg_gui_fg;
7517 else if (use_norm)
7518 *fgp = gui.def_norm_pixel;
7519 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7520 *bgp = HL_TABLE()[idx].sg_gui_bg;
7521 else if (use_norm)
7522 *bgp = gui.def_back_pixel;
7523 return TRUE;
7524 }
7525 return FALSE;
7526}
7527
7528/*
7529 * Get the font of the "Normal" group.
7530 * Returns "" when it's not found or not set.
7531 */
7532 char_u *
7533hl_get_font_name()
7534{
7535 int id;
7536 char_u *s;
7537
7538 id = syn_name2id((char_u *)"Normal");
7539 if (id > 0)
7540 {
7541 s = HL_TABLE()[id - 1].sg_font_name;
7542 if (s != NULL)
7543 return s;
7544 }
7545 return (char_u *)"";
7546}
7547
7548/*
7549 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7550 * actually chosen to be used.
7551 */
7552 void
7553hl_set_font_name(font_name)
7554 char_u *font_name;
7555{
7556 int id;
7557
7558 id = syn_name2id((char_u *)"Normal");
7559 if (id > 0)
7560 {
7561 vim_free(HL_TABLE()[id - 1].sg_font_name);
7562 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7563 }
7564}
7565
7566/*
7567 * Set background color for "Normal" group. Called by gui_set_bg_color()
7568 * when the color is known.
7569 */
7570 void
7571hl_set_bg_color_name(name)
7572 char_u *name; /* must have been allocated */
7573{
7574 int id;
7575
7576 if (name != NULL)
7577 {
7578 id = syn_name2id((char_u *)"Normal");
7579 if (id > 0)
7580 {
7581 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7582 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7583 }
7584 }
7585}
7586
7587/*
7588 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7589 * when the color is known.
7590 */
7591 void
7592hl_set_fg_color_name(name)
7593 char_u *name; /* must have been allocated */
7594{
7595 int id;
7596
7597 if (name != NULL)
7598 {
7599 id = syn_name2id((char_u *)"Normal");
7600 if (id > 0)
7601 {
7602 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7603 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7604 }
7605 }
7606}
7607
7608/*
7609 * Return the handle for a color name.
7610 * Returns INVALCOLOR when failed.
7611 */
7612 static guicolor_T
7613color_name2handle(name)
7614 char_u *name;
7615{
7616 if (STRCMP(name, "NONE") == 0)
7617 return INVALCOLOR;
7618
7619 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7620 return gui.norm_pixel;
7621 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7622 return gui.back_pixel;
7623
7624 return gui_get_color(name);
7625}
7626
7627/*
7628 * Return the handle for a font name.
7629 * Returns NOFONT when failed.
7630 */
7631 static GuiFont
7632font_name2handle(name)
7633 char_u *name;
7634{
7635 if (STRCMP(name, "NONE") == 0)
7636 return NOFONT;
7637
7638 return gui_mch_get_font(name, TRUE);
7639}
7640
7641# ifdef FEAT_XFONTSET
7642/*
7643 * Return the handle for a fontset name.
7644 * Returns NOFONTSET when failed.
7645 */
7646 static GuiFontset
7647fontset_name2handle(name, fixed_width)
7648 char_u *name;
7649 int fixed_width;
7650{
7651 if (STRCMP(name, "NONE") == 0)
7652 return NOFONTSET;
7653
7654 return gui_mch_get_fontset(name, TRUE, fixed_width);
7655}
7656# endif
7657
7658/*
7659 * Get the font or fontset for one highlight group.
7660 */
7661/*ARGSUSED*/
7662 static void
7663hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
7664 int idx;
7665 char_u *arg;
7666 int do_normal; /* set normal font */
7667 int do_menu; /* set menu font */
7668 int do_tooltip; /* set tooltip font */
7669{
7670# ifdef FEAT_XFONTSET
7671 /* If 'guifontset' is not empty, first try using the name as a
7672 * fontset. If that doesn't work, use it as a font name. */
7673 if (*p_guifontset != NUL
7674# ifdef FONTSET_ALWAYS
7675 || do_menu
7676# endif
7677# ifdef FEAT_BEVAL_TIP
7678 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
7679 || do_tooltip
7680# endif
7681 )
7682 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
7683# ifdef FONTSET_ALWAYS
7684 || do_menu
7685# endif
7686# ifdef FEAT_BEVAL_TIP
7687 || do_tooltip
7688# endif
7689 );
7690 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7691 {
7692 /* If it worked and it's the Normal group, use it as the
7693 * normal fontset. Same for the Menu group. */
7694 if (do_normal)
7695 gui_init_font(arg, TRUE);
7696# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7697 if (do_menu)
7698 {
7699# ifdef FONTSET_ALWAYS
7700 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
7701# else
7702 /* YIKES! This is a bug waiting to crash the program */
7703 gui.menu_font = HL_TABLE()[idx].sg_fontset;
7704# endif
7705 gui_mch_new_menu_font();
7706 }
7707# ifdef FEAT_BEVAL
7708 if (do_tooltip)
7709 {
7710 /* The Athena widget set cannot currently handle switching between
7711 * displaying a single font and a fontset.
7712 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007713 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00007714 * XFontStruct is used.
7715 */
7716 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
7717 gui_mch_new_tooltip_font();
7718 }
7719# endif
7720# endif
7721 }
7722 else
7723# endif
7724 {
7725 HL_TABLE()[idx].sg_font = font_name2handle(arg);
7726 /* If it worked and it's the Normal group, use it as the
7727 * normal font. Same for the Menu group. */
7728 if (HL_TABLE()[idx].sg_font != NOFONT)
7729 {
7730 if (do_normal)
7731 gui_init_font(arg, FALSE);
7732#ifndef FONTSET_ALWAYS
7733# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7734 if (do_menu)
7735 {
7736 gui.menu_font = HL_TABLE()[idx].sg_font;
7737 gui_mch_new_menu_font();
7738 }
7739# endif
7740#endif
7741 }
7742 }
7743}
7744
7745#endif /* FEAT_GUI */
7746
7747/*
7748 * Table with the specifications for an attribute number.
7749 * Note that this table is used by ALL buffers. This is required because the
7750 * GUI can redraw at any time for any buffer.
7751 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00007752static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007753
7754#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
7755
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00007756static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007757
7758#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
7759
7760#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00007761static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007762
7763#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
7764#endif
7765
7766/*
7767 * Return the attr number for a set of colors and font.
7768 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
7769 * if the combination is new.
7770 * Return 0 for error (no more room).
7771 */
7772 static int
7773get_attr_entry(table, aep)
7774 garray_T *table;
7775 attrentry_T *aep;
7776{
7777 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007778 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007779 static int recursive = FALSE;
7780
7781 /*
7782 * Init the table, in case it wasn't done yet.
7783 */
7784 table->ga_itemsize = sizeof(attrentry_T);
7785 table->ga_growsize = 7;
7786
7787 /*
7788 * Try to find an entry with the same specifications.
7789 */
7790 for (i = 0; i < table->ga_len; ++i)
7791 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007792 taep = &(((attrentry_T *)table->ga_data)[i]);
7793 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00007794 && (
7795#ifdef FEAT_GUI
7796 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007797 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
7798 && aep->ae_u.gui.bg_color
7799 == taep->ae_u.gui.bg_color
7800 && aep->ae_u.gui.sp_color
7801 == taep->ae_u.gui.sp_color
7802 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00007803# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007804 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00007805# endif
7806 ))
7807 ||
7808#endif
7809 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007810 && (aep->ae_u.term.start == NULL)
7811 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007812 && (aep->ae_u.term.start == NULL
7813 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007814 taep->ae_u.term.start) == 0)
7815 && (aep->ae_u.term.stop == NULL)
7816 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007817 && (aep->ae_u.term.stop == NULL
7818 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007819 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007820 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007821 && aep->ae_u.cterm.fg_color
7822 == taep->ae_u.cterm.fg_color
7823 && aep->ae_u.cterm.bg_color
7824 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007825 ))
7826
7827 return i + ATTR_OFF;
7828 }
7829
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00007830 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007831 {
7832 /*
7833 * Running out of attribute entries! remove all attributes, and
7834 * compute new ones for all groups.
7835 * When called recursively, we are really out of numbers.
7836 */
7837 if (recursive)
7838 {
7839 EMSG(_("E424: Too many different highlighting attributes in use"));
7840 return 0;
7841 }
7842 recursive = TRUE;
7843
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007844 clear_hl_tables();
7845
Bram Moolenaar071d4272004-06-13 20:20:40 +00007846 must_redraw = CLEAR;
7847
7848 for (i = 0; i < highlight_ga.ga_len; ++i)
7849 set_hl_attr(i);
7850
7851 recursive = FALSE;
7852 }
7853
7854 /*
7855 * This is a new combination of colors and font, add an entry.
7856 */
7857 if (ga_grow(table, 1) == FAIL)
7858 return 0;
7859
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007860 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
7861 vim_memset(taep, 0, sizeof(attrentry_T));
7862 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007863#ifdef FEAT_GUI
7864 if (table == &gui_attr_table)
7865 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007866 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
7867 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
7868 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
7869 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007870# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007871 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007872# endif
7873 }
7874#endif
7875 if (table == &term_attr_table)
7876 {
7877 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007878 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007879 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007880 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007881 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007882 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007883 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007884 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007885 }
7886 else if (table == &cterm_attr_table)
7887 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007888 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
7889 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007890 }
7891 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007892 return (table->ga_len - 1 + ATTR_OFF);
7893}
7894
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007895/*
7896 * Clear all highlight tables.
7897 */
7898 void
7899clear_hl_tables()
7900{
7901 int i;
7902 attrentry_T *taep;
7903
7904#ifdef FEAT_GUI
7905 ga_clear(&gui_attr_table);
7906#endif
7907 for (i = 0; i < term_attr_table.ga_len; ++i)
7908 {
7909 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
7910 vim_free(taep->ae_u.term.start);
7911 vim_free(taep->ae_u.term.stop);
7912 }
7913 ga_clear(&term_attr_table);
7914 ga_clear(&cterm_attr_table);
7915}
7916
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007917#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00007918/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00007919 * Combine special attributes (e.g., for spelling) with other attributes
7920 * (e.g., for syntax highlighting).
7921 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00007922 * This creates a new group when required.
7923 * Since we expect there to be few spelling mistakes we don't cache the
7924 * result.
7925 * Return the resulting attributes.
7926 */
7927 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00007928hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00007929 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00007930 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007931{
7932 attrentry_T *char_aep = NULL;
7933 attrentry_T *spell_aep;
7934 attrentry_T new_en;
7935
7936 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00007937 return prim_attr;
7938 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
7939 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007940#ifdef FEAT_GUI
7941 if (gui.in_use)
7942 {
7943 if (char_attr > HL_ALL)
7944 char_aep = syn_gui_attr2entry(char_attr);
7945 if (char_aep != NULL)
7946 new_en = *char_aep;
7947 else
7948 {
7949 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00007950 new_en.ae_u.gui.fg_color = INVALCOLOR;
7951 new_en.ae_u.gui.bg_color = INVALCOLOR;
7952 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007953 if (char_attr <= HL_ALL)
7954 new_en.ae_attr = char_attr;
7955 }
7956
Bram Moolenaar30abd282005-06-22 22:35:10 +00007957 if (prim_attr <= HL_ALL)
7958 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007959 else
7960 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00007961 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00007962 if (spell_aep != NULL)
7963 {
7964 new_en.ae_attr |= spell_aep->ae_attr;
7965 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
7966 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
7967 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
7968 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
7969 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
7970 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
7971 if (spell_aep->ae_u.gui.font != NOFONT)
7972 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
7973# ifdef FEAT_XFONTSET
7974 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
7975 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
7976# endif
7977 }
7978 }
7979 return get_attr_entry(&gui_attr_table, &new_en);
7980 }
7981#endif
7982
7983 if (t_colors > 1)
7984 {
7985 if (char_attr > HL_ALL)
7986 char_aep = syn_cterm_attr2entry(char_attr);
7987 if (char_aep != NULL)
7988 new_en = *char_aep;
7989 else
7990 {
7991 vim_memset(&new_en, 0, sizeof(new_en));
7992 if (char_attr <= HL_ALL)
7993 new_en.ae_attr = char_attr;
7994 }
7995
Bram Moolenaar30abd282005-06-22 22:35:10 +00007996 if (prim_attr <= HL_ALL)
7997 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007998 else
7999 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008000 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008001 if (spell_aep != NULL)
8002 {
8003 new_en.ae_attr |= spell_aep->ae_attr;
8004 if (spell_aep->ae_u.cterm.fg_color > 0)
8005 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8006 if (spell_aep->ae_u.cterm.bg_color > 0)
8007 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8008 }
8009 }
8010 return get_attr_entry(&cterm_attr_table, &new_en);
8011 }
8012
8013 if (char_attr > HL_ALL)
8014 char_aep = syn_term_attr2entry(char_attr);
8015 if (char_aep != NULL)
8016 new_en = *char_aep;
8017 else
8018 {
8019 vim_memset(&new_en, 0, sizeof(new_en));
8020 if (char_attr <= HL_ALL)
8021 new_en.ae_attr = char_attr;
8022 }
8023
Bram Moolenaar30abd282005-06-22 22:35:10 +00008024 if (prim_attr <= HL_ALL)
8025 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008026 else
8027 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008028 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008029 if (spell_aep != NULL)
8030 {
8031 new_en.ae_attr |= spell_aep->ae_attr;
8032 if (spell_aep->ae_u.term.start != NULL)
8033 {
8034 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8035 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8036 }
8037 }
8038 }
8039 return get_attr_entry(&term_attr_table, &new_en);
8040}
8041#endif
8042
Bram Moolenaar071d4272004-06-13 20:20:40 +00008043#ifdef FEAT_GUI
8044
8045 attrentry_T *
8046syn_gui_attr2entry(attr)
8047 int attr;
8048{
8049 attr -= ATTR_OFF;
8050 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8051 return NULL;
8052 return &(GUI_ATTR_ENTRY(attr));
8053}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008054#endif /* FEAT_GUI */
8055
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008056/*
8057 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8058 * Only to be used when "attr" > HL_ALL.
8059 */
8060 int
8061syn_attr2attr(attr)
8062 int attr;
8063{
8064 attrentry_T *aep;
8065
8066#ifdef FEAT_GUI
8067 if (gui.in_use)
8068 aep = syn_gui_attr2entry(attr);
8069 else
8070#endif
8071 if (t_colors > 1)
8072 aep = syn_cterm_attr2entry(attr);
8073 else
8074 aep = syn_term_attr2entry(attr);
8075
8076 if (aep == NULL) /* highlighting not set */
8077 return 0;
8078 return aep->ae_attr;
8079}
8080
8081
Bram Moolenaar071d4272004-06-13 20:20:40 +00008082 attrentry_T *
8083syn_term_attr2entry(attr)
8084 int attr;
8085{
8086 attr -= ATTR_OFF;
8087 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8088 return NULL;
8089 return &(TERM_ATTR_ENTRY(attr));
8090}
8091
8092 attrentry_T *
8093syn_cterm_attr2entry(attr)
8094 int attr;
8095{
8096 attr -= ATTR_OFF;
8097 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8098 return NULL;
8099 return &(CTERM_ATTR_ENTRY(attr));
8100}
8101
8102#define LIST_ATTR 1
8103#define LIST_STRING 2
8104#define LIST_INT 3
8105
8106 static void
8107highlight_list_one(id)
8108 int id;
8109{
8110 struct hl_group *sgp;
8111 int didh = FALSE;
8112
8113 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8114
8115 didh = highlight_list_arg(id, didh, LIST_ATTR,
8116 sgp->sg_term, NULL, "term");
8117 didh = highlight_list_arg(id, didh, LIST_STRING,
8118 0, sgp->sg_start, "start");
8119 didh = highlight_list_arg(id, didh, LIST_STRING,
8120 0, sgp->sg_stop, "stop");
8121
8122 didh = highlight_list_arg(id, didh, LIST_ATTR,
8123 sgp->sg_cterm, NULL, "cterm");
8124 didh = highlight_list_arg(id, didh, LIST_INT,
8125 sgp->sg_cterm_fg, NULL, "ctermfg");
8126 didh = highlight_list_arg(id, didh, LIST_INT,
8127 sgp->sg_cterm_bg, NULL, "ctermbg");
8128
8129#ifdef FEAT_GUI
8130 didh = highlight_list_arg(id, didh, LIST_ATTR,
8131 sgp->sg_gui, NULL, "gui");
8132 didh = highlight_list_arg(id, didh, LIST_STRING,
8133 0, sgp->sg_gui_fg_name, "guifg");
8134 didh = highlight_list_arg(id, didh, LIST_STRING,
8135 0, sgp->sg_gui_bg_name, "guibg");
8136 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008137 0, sgp->sg_gui_sp_name, "guisp");
8138 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008139 0, sgp->sg_font_name, "font");
8140#endif
8141
Bram Moolenaar661b1822005-07-28 22:36:45 +00008142 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008143 {
8144 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008145 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008146 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8147 msg_putchar(' ');
8148 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8149 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008150
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008151 if (!didh)
8152 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008153#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008154 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008155 last_set_msg(sgp->sg_scriptID);
8156#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008157}
8158
8159 static int
8160highlight_list_arg(id, didh, type, iarg, sarg, name)
8161 int id;
8162 int didh;
8163 int type;
8164 int iarg;
8165 char_u *sarg;
8166 char *name;
8167{
8168 char_u buf[100];
8169 char_u *ts;
8170 int i;
8171
Bram Moolenaar661b1822005-07-28 22:36:45 +00008172 if (got_int)
8173 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008174 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8175 {
8176 ts = buf;
8177 if (type == LIST_INT)
8178 sprintf((char *)buf, "%d", iarg - 1);
8179 else if (type == LIST_STRING)
8180 ts = sarg;
8181 else /* type == LIST_ATTR */
8182 {
8183 buf[0] = NUL;
8184 for (i = 0; hl_attr_table[i] != 0; ++i)
8185 {
8186 if (iarg & hl_attr_table[i])
8187 {
8188 if (buf[0] != NUL)
8189 STRCAT(buf, ",");
8190 STRCAT(buf, hl_name_table[i]);
8191 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8192 }
8193 }
8194 }
8195
8196 (void)syn_list_header(didh,
8197 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8198 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008199 if (!got_int)
8200 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008201 if (*name != NUL)
8202 {
8203 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8204 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8205 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008206 msg_outtrans(ts);
8207 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008208 }
8209 return didh;
8210}
8211
8212#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8213/*
8214 * Return "1" if highlight group "id" has attribute "flag".
8215 * Return NULL otherwise.
8216 */
8217 char_u *
8218highlight_has_attr(id, flag, modec)
8219 int id;
8220 int flag;
8221 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8222{
8223 int attr;
8224
8225 if (id <= 0 || id > highlight_ga.ga_len)
8226 return NULL;
8227
8228#ifdef FEAT_GUI
8229 if (modec == 'g')
8230 attr = HL_TABLE()[id - 1].sg_gui;
8231 else
8232#endif
8233 if (modec == 'c')
8234 attr = HL_TABLE()[id - 1].sg_cterm;
8235 else
8236 attr = HL_TABLE()[id - 1].sg_term;
8237
8238 if (attr & flag)
8239 return (char_u *)"1";
8240 return NULL;
8241}
8242#endif
8243
8244#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8245/*
8246 * Return color name of highlight group "id".
8247 */
8248 char_u *
8249highlight_color(id, what, modec)
8250 int id;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008251 char_u *what; /* "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008252 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8253{
8254 static char_u name[20];
8255 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008256 int fg = FALSE;
8257# ifdef FEAT_GUI
8258 int sp = FALSE;
8259# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008260
8261 if (id <= 0 || id > highlight_ga.ga_len)
8262 return NULL;
8263
8264 if (TOLOWER_ASC(what[0]) == 'f')
8265 fg = TRUE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008266# ifdef FEAT_GUI
8267 else if (TOLOWER_ASC(what[0]) == 's')
8268 sp = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008269 if (modec == 'g')
8270 {
8271 /* return #RRGGBB form (only possible when GUI is running) */
8272 if (gui.in_use && what[1] && what[2] == '#')
8273 {
8274 guicolor_T color;
8275 long_u rgb;
8276 static char_u buf[10];
8277
8278 if (fg)
8279 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008280 else if (sp)
8281 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008282 else
8283 color = HL_TABLE()[id - 1].sg_gui_bg;
8284 if (color == INVALCOLOR)
8285 return NULL;
8286 rgb = gui_mch_get_rgb(color);
8287 sprintf((char *)buf, "#%02x%02x%02x",
8288 (unsigned)(rgb >> 16),
8289 (unsigned)(rgb >> 8) & 255,
8290 (unsigned)rgb & 255);
8291 return buf;
8292 }
8293 if (fg)
8294 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008295 if (sp)
8296 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008297 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8298 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008299# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008300 if (modec == 'c')
8301 {
8302 if (fg)
8303 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8304 else
8305 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8306 sprintf((char *)name, "%d", n);
8307 return name;
8308 }
8309 /* term doesn't have color */
8310 return NULL;
8311}
8312#endif
8313
8314#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8315 || defined(PROTO)
8316/*
8317 * Return color name of highlight group "id" as RGB value.
8318 */
8319 long_u
8320highlight_gui_color_rgb(id, fg)
8321 int id;
8322 int fg; /* TRUE = fg, FALSE = bg */
8323{
8324 guicolor_T color;
8325
8326 if (id <= 0 || id > highlight_ga.ga_len)
8327 return 0L;
8328
8329 if (fg)
8330 color = HL_TABLE()[id - 1].sg_gui_fg;
8331 else
8332 color = HL_TABLE()[id - 1].sg_gui_bg;
8333
8334 if (color == INVALCOLOR)
8335 return 0L;
8336
8337 return gui_mch_get_rgb(color);
8338}
8339#endif
8340
8341/*
8342 * Output the syntax list header.
8343 * Return TRUE when started a new line.
8344 */
8345 static int
8346syn_list_header(did_header, outlen, id)
8347 int did_header; /* did header already */
8348 int outlen; /* length of string that comes */
8349 int id; /* highlight group id */
8350{
8351 int endcol = 19;
8352 int newline = TRUE;
8353
8354 if (!did_header)
8355 {
8356 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008357 if (got_int)
8358 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008359 msg_outtrans(HL_TABLE()[id - 1].sg_name);
8360 endcol = 15;
8361 }
8362 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008363 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008364 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008365 if (got_int)
8366 return TRUE;
8367 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008368 else
8369 {
8370 if (msg_col >= endcol) /* wrap around is like starting a new line */
8371 newline = FALSE;
8372 }
8373
8374 if (msg_col >= endcol) /* output at least one space */
8375 endcol = msg_col + 1;
8376 if (Columns <= endcol) /* avoid hang for tiny window */
8377 endcol = Columns - 1;
8378
8379 msg_advance(endcol);
8380
8381 /* Show "xxx" with the attributes. */
8382 if (!did_header)
8383 {
8384 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8385 msg_putchar(' ');
8386 }
8387
8388 return newline;
8389}
8390
8391/*
8392 * Set the attribute numbers for a highlight group.
8393 * Called after one of the attributes has changed.
8394 */
8395 static void
8396set_hl_attr(idx)
8397 int idx; /* index in array */
8398{
8399 attrentry_T at_en;
8400 struct hl_group *sgp = HL_TABLE() + idx;
8401
8402 /* The "Normal" group doesn't need an attribute number */
8403 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8404 return;
8405
8406#ifdef FEAT_GUI
8407 /*
8408 * For the GUI mode: If there are other than "normal" highlighting
8409 * attributes, need to allocate an attr number.
8410 */
8411 if (sgp->sg_gui_fg == INVALCOLOR
8412 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008413 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008414 && sgp->sg_font == NOFONT
8415# ifdef FEAT_XFONTSET
8416 && sgp->sg_fontset == NOFONTSET
8417# endif
8418 )
8419 {
8420 sgp->sg_gui_attr = sgp->sg_gui;
8421 }
8422 else
8423 {
8424 at_en.ae_attr = sgp->sg_gui;
8425 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8426 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008427 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008428 at_en.ae_u.gui.font = sgp->sg_font;
8429# ifdef FEAT_XFONTSET
8430 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8431# endif
8432 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8433 }
8434#endif
8435 /*
8436 * For the term mode: If there are other than "normal" highlighting
8437 * attributes, need to allocate an attr number.
8438 */
8439 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8440 sgp->sg_term_attr = sgp->sg_term;
8441 else
8442 {
8443 at_en.ae_attr = sgp->sg_term;
8444 at_en.ae_u.term.start = sgp->sg_start;
8445 at_en.ae_u.term.stop = sgp->sg_stop;
8446 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8447 }
8448
8449 /*
8450 * For the color term mode: If there are other than "normal"
8451 * highlighting attributes, need to allocate an attr number.
8452 */
8453 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8454 sgp->sg_cterm_attr = sgp->sg_cterm;
8455 else
8456 {
8457 at_en.ae_attr = sgp->sg_cterm;
8458 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8459 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8460 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8461 }
8462}
8463
8464/*
8465 * Lookup a highlight group name and return it's ID.
8466 * If it is not found, 0 is returned.
8467 */
8468 int
8469syn_name2id(name)
8470 char_u *name;
8471{
8472 int i;
8473 char_u name_u[200];
8474
8475 /* Avoid using stricmp() too much, it's slow on some systems */
8476 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8477 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00008478 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008479 vim_strup(name_u);
8480 for (i = highlight_ga.ga_len; --i >= 0; )
8481 if (HL_TABLE()[i].sg_name_u != NULL
8482 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8483 break;
8484 return i + 1;
8485}
8486
8487#if defined(FEAT_EVAL) || defined(PROTO)
8488/*
8489 * Return TRUE if highlight group "name" exists.
8490 */
8491 int
8492highlight_exists(name)
8493 char_u *name;
8494{
8495 return (syn_name2id(name) > 0);
8496}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008497
8498# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
8499/*
8500 * Return the name of highlight group "id".
8501 * When not a valid ID return an empty string.
8502 */
8503 char_u *
8504syn_id2name(id)
8505 int id;
8506{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00008507 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008508 return (char_u *)"";
8509 return HL_TABLE()[id - 1].sg_name;
8510}
8511# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008512#endif
8513
8514/*
8515 * Like syn_name2id(), but take a pointer + length argument.
8516 */
8517 int
8518syn_namen2id(linep, len)
8519 char_u *linep;
8520 int len;
8521{
8522 char_u *name;
8523 int id = 0;
8524
8525 name = vim_strnsave(linep, len);
8526 if (name != NULL)
8527 {
8528 id = syn_name2id(name);
8529 vim_free(name);
8530 }
8531 return id;
8532}
8533
8534/*
8535 * Find highlight group name in the table and return it's ID.
8536 * The argument is a pointer to the name and the length of the name.
8537 * If it doesn't exist yet, a new entry is created.
8538 * Return 0 for failure.
8539 */
8540 int
8541syn_check_group(pp, len)
8542 char_u *pp;
8543 int len;
8544{
8545 int id;
8546 char_u *name;
8547
8548 name = vim_strnsave(pp, len);
8549 if (name == NULL)
8550 return 0;
8551
8552 id = syn_name2id(name);
8553 if (id == 0) /* doesn't exist yet */
8554 id = syn_add_group(name);
8555 else
8556 vim_free(name);
8557 return id;
8558}
8559
8560/*
8561 * Add new highlight group and return it's ID.
8562 * "name" must be an allocated string, it will be consumed.
8563 * Return 0 for failure.
8564 */
8565 static int
8566syn_add_group(name)
8567 char_u *name;
8568{
8569 char_u *p;
8570
8571 /* Check that the name is ASCII letters, digits and underscore. */
8572 for (p = name; *p != NUL; ++p)
8573 {
8574 if (!vim_isprintc(*p))
8575 {
8576 EMSG(_("E669: Unprintable character in group name"));
8577 return 0;
8578 }
8579 else if (!ASCII_ISALNUM(*p) && *p != '_')
8580 {
8581 /* This is an error, but since there previously was no check only
8582 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008583 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008584 MSG(_("W18: Invalid character in group name"));
8585 break;
8586 }
8587 }
8588
8589 /*
8590 * First call for this growarray: init growing array.
8591 */
8592 if (highlight_ga.ga_data == NULL)
8593 {
8594 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8595 highlight_ga.ga_growsize = 10;
8596 }
8597
8598 /*
8599 * Make room for at least one other syntax_highlight entry.
8600 */
8601 if (ga_grow(&highlight_ga, 1) == FAIL)
8602 {
8603 vim_free(name);
8604 return 0;
8605 }
8606
8607 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8608 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8609 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8610#ifdef FEAT_GUI
8611 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8612 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008613 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008614#endif
8615 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008616
8617 return highlight_ga.ga_len; /* ID is index plus one */
8618}
8619
8620/*
8621 * When, just after calling syn_add_group(), an error is discovered, this
8622 * function deletes the new name.
8623 */
8624 static void
8625syn_unadd_group()
8626{
8627 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008628 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8629 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8630}
8631
8632/*
8633 * Translate a group ID to highlight attributes.
8634 */
8635 int
8636syn_id2attr(hl_id)
8637 int hl_id;
8638{
8639 int attr;
8640 struct hl_group *sgp;
8641
8642 hl_id = syn_get_final_id(hl_id);
8643 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8644
8645#ifdef FEAT_GUI
8646 /*
8647 * Only use GUI attr when the GUI is being used.
8648 */
8649 if (gui.in_use)
8650 attr = sgp->sg_gui_attr;
8651 else
8652#endif
8653 if (t_colors > 1)
8654 attr = sgp->sg_cterm_attr;
8655 else
8656 attr = sgp->sg_term_attr;
8657
8658 return attr;
8659}
8660
8661#ifdef FEAT_GUI
8662/*
8663 * Get the GUI colors and attributes for a group ID.
8664 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
8665 */
8666 int
8667syn_id2colors(hl_id, fgp, bgp)
8668 int hl_id;
8669 guicolor_T *fgp;
8670 guicolor_T *bgp;
8671{
8672 struct hl_group *sgp;
8673
8674 hl_id = syn_get_final_id(hl_id);
8675 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8676
8677 *fgp = sgp->sg_gui_fg;
8678 *bgp = sgp->sg_gui_bg;
8679 return sgp->sg_gui;
8680}
8681#endif
8682
8683/*
8684 * Translate a group ID to the final group ID (following links).
8685 */
8686 int
8687syn_get_final_id(hl_id)
8688 int hl_id;
8689{
8690 int count;
8691 struct hl_group *sgp;
8692
8693 if (hl_id > highlight_ga.ga_len || hl_id < 1)
8694 return 0; /* Can be called from eval!! */
8695
8696 /*
8697 * Follow links until there is no more.
8698 * Look out for loops! Break after 100 links.
8699 */
8700 for (count = 100; --count >= 0; )
8701 {
8702 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8703 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
8704 break;
8705 hl_id = sgp->sg_link;
8706 }
8707
8708 return hl_id;
8709}
8710
8711#ifdef FEAT_GUI
8712/*
8713 * Call this function just after the GUI has started.
8714 * It finds the font and color handles for the highlighting groups.
8715 */
8716 void
8717highlight_gui_started()
8718{
8719 int idx;
8720
8721 /* First get the colors from the "Normal" and "Menu" group, if set */
8722 set_normal_colors();
8723
8724 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8725 gui_do_one_color(idx, FALSE, FALSE);
8726
8727 highlight_changed();
8728}
8729
8730 static void
8731gui_do_one_color(idx, do_menu, do_tooltip)
8732 int idx;
8733 int do_menu; /* TRUE: might set the menu font */
8734 int do_tooltip; /* TRUE: might set the tooltip font */
8735{
8736 int didit = FALSE;
8737
8738 if (HL_TABLE()[idx].sg_font_name != NULL)
8739 {
8740 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
8741 do_tooltip);
8742 didit = TRUE;
8743 }
8744 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
8745 {
8746 HL_TABLE()[idx].sg_gui_fg =
8747 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
8748 didit = TRUE;
8749 }
8750 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
8751 {
8752 HL_TABLE()[idx].sg_gui_bg =
8753 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
8754 didit = TRUE;
8755 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008756 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
8757 {
8758 HL_TABLE()[idx].sg_gui_sp =
8759 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
8760 didit = TRUE;
8761 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008762 if (didit) /* need to get a new attr number */
8763 set_hl_attr(idx);
8764}
8765
8766#endif
8767
8768/*
8769 * Translate the 'highlight' option into attributes in highlight_attr[] and
8770 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
8771 * corresponding highlights to use on top of HLF_SNC is computed.
8772 * Called only when the 'highlight' option has been changed and upon first
8773 * screen redraw after any :highlight command.
8774 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
8775 */
8776 int
8777highlight_changed()
8778{
8779 int hlf;
8780 int i;
8781 char_u *p;
8782 int attr;
8783 char_u *end;
8784 int id;
8785#ifdef USER_HIGHLIGHT
8786 char_u userhl[10];
8787# ifdef FEAT_STL_OPT
8788 int id_SNC = -1;
8789 int id_S = -1;
8790 int hlcnt;
8791# endif
8792#endif
8793 static int hl_flags[HLF_COUNT] = HL_FLAGS;
8794
8795 need_highlight_changed = FALSE;
8796
8797 /*
8798 * Clear all attributes.
8799 */
8800 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8801 highlight_attr[hlf] = 0;
8802
8803 /*
8804 * First set all attributes to their default value.
8805 * Then use the attributes from the 'highlight' option.
8806 */
8807 for (i = 0; i < 2; ++i)
8808 {
8809 if (i)
8810 p = p_hl;
8811 else
8812 p = get_highlight_default();
8813 if (p == NULL) /* just in case */
8814 continue;
8815
8816 while (*p)
8817 {
8818 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8819 if (hl_flags[hlf] == *p)
8820 break;
8821 ++p;
8822 if (hlf == (int)HLF_COUNT || *p == NUL)
8823 return FAIL;
8824
8825 /*
8826 * Allow several hl_flags to be combined, like "bu" for
8827 * bold-underlined.
8828 */
8829 attr = 0;
8830 for ( ; *p && *p != ','; ++p) /* parse upto comma */
8831 {
8832 if (vim_iswhite(*p)) /* ignore white space */
8833 continue;
8834
8835 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
8836 return FAIL;
8837
8838 switch (*p)
8839 {
8840 case 'b': attr |= HL_BOLD;
8841 break;
8842 case 'i': attr |= HL_ITALIC;
8843 break;
8844 case '-':
8845 case 'n': /* no highlighting */
8846 break;
8847 case 'r': attr |= HL_INVERSE;
8848 break;
8849 case 's': attr |= HL_STANDOUT;
8850 break;
8851 case 'u': attr |= HL_UNDERLINE;
8852 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008853 case 'c': attr |= HL_UNDERCURL;
8854 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008855 case ':': ++p; /* highlight group name */
8856 if (attr || *p == NUL) /* no combinations */
8857 return FAIL;
8858 end = vim_strchr(p, ',');
8859 if (end == NULL)
8860 end = p + STRLEN(p);
8861 id = syn_check_group(p, (int)(end - p));
8862 if (id == 0)
8863 return FAIL;
8864 attr = syn_id2attr(id);
8865 p = end - 1;
8866#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
8867 if (hlf == (int)HLF_SNC)
8868 id_SNC = syn_get_final_id(id);
8869 else if (hlf == (int)HLF_S)
8870 id_S = syn_get_final_id(id);
8871#endif
8872 break;
8873 default: return FAIL;
8874 }
8875 }
8876 highlight_attr[hlf] = attr;
8877
8878 p = skip_to_option_part(p); /* skip comma and spaces */
8879 }
8880 }
8881
8882#ifdef USER_HIGHLIGHT
8883 /* Setup the user highlights
8884 *
8885 * Temporarily utilize 10 more hl entries. Have to be in there
8886 * simultaneously in case of table overflows in get_attr_entry()
8887 */
8888# ifdef FEAT_STL_OPT
8889 if (ga_grow(&highlight_ga, 10) == FAIL)
8890 return FAIL;
8891 hlcnt = highlight_ga.ga_len;
8892 if (id_S == 0)
8893 { /* Make sure id_S is always valid to simplify code below */
8894 memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
8895 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
8896 id_S = hlcnt + 10;
8897 }
8898# endif
8899 for (i = 0; i < 9; i++)
8900 {
8901 sprintf((char *)userhl, "User%d", i + 1);
8902 id = syn_name2id(userhl);
8903 if (id == 0)
8904 {
8905 highlight_user[i] = 0;
8906# ifdef FEAT_STL_OPT
8907 highlight_stlnc[i] = 0;
8908# endif
8909 }
8910 else
8911 {
8912# ifdef FEAT_STL_OPT
8913 struct hl_group *hlt = HL_TABLE();
8914# endif
8915
8916 highlight_user[i] = syn_id2attr(id);
8917# ifdef FEAT_STL_OPT
8918 if (id_SNC == 0)
8919 {
8920 memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
8921 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
8922 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
8923# ifdef FEAT_GUI
8924 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
8925# endif
8926 }
8927 else
8928 mch_memmove(&hlt[hlcnt + i],
8929 &hlt[id_SNC - 1],
8930 sizeof(struct hl_group));
8931 hlt[hlcnt + i].sg_link = 0;
8932
8933 /* Apply difference between UserX and HLF_S to HLF_SNC */
8934 hlt[hlcnt + i].sg_term ^=
8935 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
8936 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
8937 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
8938 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
8939 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
8940 hlt[hlcnt + i].sg_cterm ^=
8941 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
8942 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
8943 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
8944 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
8945 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
8946# ifdef FEAT_GUI
8947 hlt[hlcnt + i].sg_gui ^=
8948 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
8949 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
8950 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
8951 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
8952 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008953 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
8954 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008955 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
8956 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
8957# ifdef FEAT_XFONTSET
8958 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
8959 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
8960# endif
8961# endif
8962 highlight_ga.ga_len = hlcnt + i + 1;
8963 set_hl_attr(hlcnt + i); /* At long last we can apply */
8964 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
8965# endif
8966 }
8967 }
8968# ifdef FEAT_STL_OPT
8969 highlight_ga.ga_len = hlcnt;
8970# endif
8971
8972#endif /* USER_HIGHLIGHT */
8973
8974 return OK;
8975}
8976
Bram Moolenaar4f688582007-07-24 12:34:30 +00008977#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008978
8979static void highlight_list __ARGS((void));
8980static void highlight_list_two __ARGS((int cnt, int attr));
8981
8982/*
8983 * Handle command line completion for :highlight command.
8984 */
8985 void
8986set_context_in_highlight_cmd(xp, arg)
8987 expand_T *xp;
8988 char_u *arg;
8989{
8990 char_u *p;
8991
8992 /* Default: expand group names */
8993 xp->xp_context = EXPAND_HIGHLIGHT;
8994 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00008995 include_link = 2;
8996 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008997
8998 /* (part of) subcommand already typed */
8999 if (*arg != NUL)
9000 {
9001 p = skiptowhite(arg);
9002 if (*p != NUL) /* past "default" or group name */
9003 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009004 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009005 if (STRNCMP("default", arg, p - arg) == 0)
9006 {
9007 arg = skipwhite(p);
9008 xp->xp_pattern = arg;
9009 p = skiptowhite(arg);
9010 }
9011 if (*p != NUL) /* past group name */
9012 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009013 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009014 if (arg[1] == 'i' && arg[0] == 'N')
9015 highlight_list();
9016 if (STRNCMP("link", arg, p - arg) == 0
9017 || STRNCMP("clear", arg, p - arg) == 0)
9018 {
9019 xp->xp_pattern = skipwhite(p);
9020 p = skiptowhite(xp->xp_pattern);
9021 if (*p != NUL) /* past first group name */
9022 {
9023 xp->xp_pattern = skipwhite(p);
9024 p = skiptowhite(xp->xp_pattern);
9025 }
9026 }
9027 if (*p != NUL) /* past group name(s) */
9028 xp->xp_context = EXPAND_NOTHING;
9029 }
9030 }
9031 }
9032}
9033
9034/*
9035 * List highlighting matches in a nice way.
9036 */
9037 static void
9038highlight_list()
9039{
9040 int i;
9041
9042 for (i = 10; --i >= 0; )
9043 highlight_list_two(i, hl_attr(HLF_D));
9044 for (i = 40; --i >= 0; )
9045 highlight_list_two(99, 0);
9046}
9047
9048 static void
9049highlight_list_two(cnt, attr)
9050 int cnt;
9051 int attr;
9052{
9053 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
9054 msg_clr_eos();
9055 out_flush();
9056 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9057}
9058
9059#endif /* FEAT_CMDL_COMPL */
9060
9061#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9062 || defined(FEAT_SIGNS) || defined(PROTO)
9063/*
9064 * Function given to ExpandGeneric() to obtain the list of group names.
9065 * Also used for synIDattr() function.
9066 */
9067/*ARGSUSED*/
9068 char_u *
9069get_highlight_name(xp, idx)
9070 expand_T *xp;
9071 int idx;
9072{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009073#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009074 if (idx == highlight_ga.ga_len && include_none != 0)
9075 return (char_u *)"none";
9076 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009077 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009078 if (idx == highlight_ga.ga_len + include_none + include_default
9079 && include_link != 0)
9080 return (char_u *)"link";
9081 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9082 && include_link != 0)
9083 return (char_u *)"clear";
9084#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009085 if (idx < 0 || idx >= highlight_ga.ga_len)
9086 return NULL;
9087 return HL_TABLE()[idx].sg_name;
9088}
9089#endif
9090
Bram Moolenaar4f688582007-07-24 12:34:30 +00009091#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009092/*
9093 * Free all the highlight group fonts.
9094 * Used when quitting for systems which need it.
9095 */
9096 void
9097free_highlight_fonts()
9098{
9099 int idx;
9100
9101 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9102 {
9103 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9104 HL_TABLE()[idx].sg_font = NOFONT;
9105# ifdef FEAT_XFONTSET
9106 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9107 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9108# endif
9109 }
9110
9111 gui_mch_free_font(gui.norm_font);
9112# ifdef FEAT_XFONTSET
9113 gui_mch_free_fontset(gui.fontset);
9114# endif
9115# ifndef HAVE_GTK2
9116 gui_mch_free_font(gui.bold_font);
9117 gui_mch_free_font(gui.ital_font);
9118 gui_mch_free_font(gui.boldital_font);
9119# endif
9120}
9121#endif
9122
9123/**************************************
9124 * End of Highlighting stuff *
9125 **************************************/