blob: c54984c972e821798b4614df57fc3e43253741fb [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 */
Bram Moolenaar071d4272004-06-13 20:20:40 +000038 guicolor_T sg_gui_fg; /* GUI foreground color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000039 guicolor_T sg_gui_bg; /* GUI background color handle */
Bram Moolenaare2cc9702005-03-15 22:43:58 +000040 guicolor_T sg_gui_sp; /* GUI special color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000041 GuiFont sg_font; /* GUI font handle */
42#ifdef FEAT_XFONTSET
43 GuiFontset sg_fontset; /* GUI fontset handle */
44#endif
45 char_u *sg_font_name; /* GUI font or fontset name */
46 int sg_gui_attr; /* Screen attr for GUI mode */
47#endif
Bram Moolenaar61623362010-07-14 22:04:22 +020048#if defined(FEAT_GUI) || defined(FEAT_EVAL)
49/* Store the sp color name for the GUI or synIDattr() */
50 int sg_gui; /* "gui=" highlighting attributes */
51 char_u *sg_gui_fg_name;/* GUI foreground color name */
52 char_u *sg_gui_bg_name;/* GUI background color name */
53 char_u *sg_gui_sp_name;/* GUI special color name */
54#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000055 int sg_link; /* link to this highlight group ID */
56 int sg_set; /* combination of SG_* flags */
Bram Moolenaar661b1822005-07-28 22:36:45 +000057#ifdef FEAT_EVAL
58 scid_T sg_scriptID; /* script in which the group was last set */
59#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000060};
61
62#define SG_TERM 1 /* term has been set */
63#define SG_CTERM 2 /* cterm has been set */
64#define SG_GUI 4 /* gui has been set */
65#define SG_LINK 8 /* link has been set */
66
67static garray_T highlight_ga; /* highlight groups for 'highlight' option */
68
69#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
70
71#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000072/* Flags to indicate an additional string for highlight name completion. */
73static int include_none = 0; /* when 1 include "None" */
74static int include_default = 0; /* when 1 include "default" */
75static int include_link = 0; /* when 2 include "link" and "clear" */
Bram Moolenaar071d4272004-06-13 20:20:40 +000076#endif
77
78/*
79 * The "term", "cterm" and "gui" arguments can be any combination of the
80 * following names, separated by commas (but no spaces!).
81 */
82static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000083 {"bold", "standout", "underline", "undercurl",
84 "italic", "reverse", "inverse", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000085static int hl_attr_table[] =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000086 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +000087
88static int get_attr_entry __ARGS((garray_T *table, attrentry_T *aep));
89static void syn_unadd_group __ARGS((void));
90static void set_hl_attr __ARGS((int idx));
91static void highlight_list_one __ARGS((int id));
92static int highlight_list_arg __ARGS((int id, int didh, int type, int iarg, char_u *sarg, char *name));
93static int syn_add_group __ARGS((char_u *name));
94static int syn_list_header __ARGS((int did_header, int outlen, int id));
95static int hl_has_settings __ARGS((int idx, int check_link));
96static void highlight_clear __ARGS((int idx));
97
98#ifdef FEAT_GUI
99static void gui_do_one_color __ARGS((int idx, int do_menu, int do_tooltip));
100static int set_group_colors __ARGS((char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip));
101static guicolor_T color_name2handle __ARGS((char_u *name));
102static GuiFont font_name2handle __ARGS((char_u *name));
103# ifdef FEAT_XFONTSET
104static GuiFontset fontset_name2handle __ARGS((char_u *name, int fixed_width));
105# endif
106static void hl_do_font __ARGS((int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip));
107#endif
108
109/*
110 * An attribute number is the index in attr_table plus ATTR_OFF.
111 */
112#define ATTR_OFF (HL_ALL + 1)
113
114#if defined(FEAT_SYN_HL) || defined(PROTO)
115
116#define SYN_NAMELEN 50 /* maximum length of a syntax name */
117
118/* different types of offsets that are possible */
119#define SPO_MS_OFF 0 /* match start offset */
120#define SPO_ME_OFF 1 /* match end offset */
121#define SPO_HS_OFF 2 /* highl. start offset */
122#define SPO_HE_OFF 3 /* highl. end offset */
123#define SPO_RS_OFF 4 /* region start offset */
124#define SPO_RE_OFF 5 /* region end offset */
125#define SPO_LC_OFF 6 /* leading context offset */
126#define SPO_COUNT 7
127
128static char *(spo_name_tab[SPO_COUNT]) =
129 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
130
131/*
132 * The patterns that are being searched for are stored in a syn_pattern.
133 * A match item consists of one pattern.
134 * A start/end item consists of n start patterns and m end patterns.
135 * A start/skip/end item consists of n start patterns, one skip pattern and m
136 * end patterns.
137 * For the latter two, the patterns are always consecutive: start-skip-end.
138 *
139 * A character offset can be given for the matched text (_m_start and _m_end)
140 * and for the actually highlighted text (_h_start and _h_end).
141 */
142typedef struct syn_pattern
143{
144 char sp_type; /* see SPTYPE_ defines below */
145 char sp_syncing; /* this item used for syncing */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200146 int sp_flags; /* see HL_ defines below */
147#ifdef FEAT_CONCEAL
148 int sp_char; /* conceal substitute character */
149#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000150 struct sp_syn sp_syn; /* struct passed to in_id_list() */
151 short sp_syn_match_id; /* highlight group ID of pattern */
152 char_u *sp_pattern; /* regexp to match, pattern */
153 regprog_T *sp_prog; /* regexp to match, program */
154 int sp_ic; /* ignore-case flag for sp_prog */
155 short sp_off_flags; /* see below */
156 int sp_offsets[SPO_COUNT]; /* offsets */
157 short *sp_cont_list; /* cont. group IDs, if non-zero */
158 short *sp_next_list; /* next group IDs, if non-zero */
159 int sp_sync_idx; /* sync item index (syncing only) */
160 int sp_line_id; /* ID of last line where tried */
161 int sp_startcol; /* next match in sp_line_id line */
162} synpat_T;
163
164/* The sp_off_flags are computed like this:
165 * offset from the start of the matched text: (1 << SPO_XX_OFF)
166 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
167 * When both are present, only one is used.
168 */
169
170#define SPTYPE_MATCH 1 /* match keyword with this group ID */
171#define SPTYPE_START 2 /* match a regexp, start of item */
172#define SPTYPE_END 3 /* match a regexp, end of item */
173#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
174
Bram Moolenaar071d4272004-06-13 20:20:40 +0000175
176#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
177
178#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
179
180/*
181 * Flags for b_syn_sync_flags:
182 */
183#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
184#define SF_MATCH 0x02 /* sync by matching a pattern */
185
186#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
187
Bram Moolenaar071d4272004-06-13 20:20:40 +0000188#define MAXKEYWLEN 80 /* maximum length of a keyword */
189
190/*
191 * The attributes of the syntax item that has been recognized.
192 */
193static int current_attr = 0; /* attr of current syntax word */
194#ifdef FEAT_EVAL
195static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000196static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000197#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200198#ifdef FEAT_CONCEAL
199static int current_flags = 0;
200static int current_sub_char = 0;
201#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000202
Bram Moolenaar217ad922005-03-20 22:37:15 +0000203typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000204{
205 char_u *scl_name; /* syntax cluster name */
206 char_u *scl_name_u; /* uppercase of scl_name */
207 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000208} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000209
210/*
211 * Methods of combining two clusters
212 */
213#define CLUSTER_REPLACE 1 /* replace first list with second */
214#define CLUSTER_ADD 2 /* add second list to first */
215#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
216
Bram Moolenaar217ad922005-03-20 22:37:15 +0000217#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000218
219/*
220 * Syntax group IDs have different types:
221 * 0 - 9999 normal syntax groups
222 * 10000 - 14999 ALLBUT indicator (current_syn_inc_tag added)
223 * 15000 - 19999 TOP indicator (current_syn_inc_tag added)
224 * 20000 - 24999 CONTAINED indicator (current_syn_inc_tag added)
225 * >= 25000 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
226 */
227#define SYNID_ALLBUT 10000 /* syntax group ID for contains=ALLBUT */
228#define SYNID_TOP 15000 /* syntax group ID for contains=TOP */
229#define SYNID_CONTAINED 20000 /* syntax group ID for contains=CONTAINED */
230#define SYNID_CLUSTER 25000 /* first syntax group ID for clusters */
231
232/*
233 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
234 * expand_filename(). Most of the other syntax commands don't need it, so
235 * instead of passing it to them, we stow it here.
236 */
237static char_u **syn_cmdlinep;
238
239/*
240 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200241 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000242 * rules in each ":syn include"'d file.
243 */
244static int current_syn_inc_tag = 0;
245static int running_syn_inc_tag = 0;
246
247/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000248 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
249 * This avoids adding a pointer to the hashtable item.
250 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
251 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
252 * HI2KE() converts a hashitem pointer to a var pointer.
253 */
254static keyentry_T dumkey;
255#define KE2HIKEY(kp) ((kp)->keyword)
256#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
257#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
258
259/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000260 * To reduce the time spent in keepend(), remember at which level in the state
261 * stack the first item with "keepend" is present. When "-1", there is no
262 * "keepend" on the stack.
263 */
264static int keepend_level = -1;
265
266/*
267 * For the current state we need to remember more than just the idx.
268 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
269 * (The end positions have the column number of the next char)
270 */
271typedef struct state_item
272{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000273 int si_idx; /* index of syntax pattern or
274 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000275 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000276 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000277 int si_m_lnum; /* lnum of the match */
278 int si_m_startcol; /* starting column of the match */
279 lpos_T si_m_endpos; /* just after end posn of the match */
280 lpos_T si_h_startpos; /* start position of the highlighting */
281 lpos_T si_h_endpos; /* end position of the highlighting */
282 lpos_T si_eoe_pos; /* end position of end pattern */
283 int si_end_idx; /* group ID for end pattern or zero */
284 int si_ends; /* if match ends before si_m_endpos */
285 int si_attr; /* attributes in this state */
286 long si_flags; /* HL_HAS_EOL flag in this state, and
287 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200288#ifdef FEAT_CONCEAL
289 int si_char; /* substitution character for conceal */
290#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000291 short *si_cont_list; /* list of contained groups */
292 short *si_next_list; /* nextgroup IDs after this item ends */
293 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
294 * pattern */
295} stateitem_T;
296
297#define KEYWORD_IDX -1 /* value of si_idx for keywords */
298#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
299 but contained groups */
300
301/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000302 * Struct to reduce the number of arguments to get_syn_options(), it's used
303 * very often.
304 */
305typedef struct
306{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000307 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000308 int keyword; /* TRUE for ":syn keyword" */
309 int *sync_idx; /* syntax item for "grouphere" argument, NULL
310 if not allowed */
311 char has_cont_list; /* TRUE if "cont_list" can be used */
312 short *cont_list; /* group IDs for "contains" argument */
313 short *cont_in_list; /* group IDs for "containedin" argument */
314 short *next_list; /* group IDs for "nextgroup" argument */
315} syn_opt_arg_T;
316
317/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000318 * The next possible match in the current line for any pattern is remembered,
319 * to avoid having to try for a match in each column.
320 * If next_match_idx == -1, not tried (in this line) yet.
321 * If next_match_col == MAXCOL, no match found in this line.
322 * (All end positions have the column of the char after the end)
323 */
324static int next_match_col; /* column for start of next match */
325static lpos_T next_match_m_endpos; /* position for end of next match */
326static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
327static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
328static int next_match_idx; /* index of matched item */
329static long next_match_flags; /* flags for next match */
330static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
331static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
332static int next_match_end_idx; /* ID of group for end pattn or zero */
333static reg_extmatch_T *next_match_extmatch = NULL;
334
335/*
336 * A state stack is an array of integers or stateitem_T, stored in a
337 * garray_T. A state stack is invalid if it's itemsize entry is zero.
338 */
339#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
340#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
341
342/*
343 * The current state (within the line) of the recognition engine.
344 * When current_state.ga_itemsize is 0 the current state is invalid.
345 */
346static win_T *syn_win; /* current window for highlighting */
347static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200348static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000349static linenr_T current_lnum = 0; /* lnum of current state */
350static colnr_T current_col = 0; /* column of current state */
351static int current_state_stored = 0; /* TRUE if stored current state
352 * after setting current_finished */
353static int current_finished = 0; /* current line has been finished */
354static garray_T current_state /* current stack of state_items */
355 = {0, 0, 0, 0, NULL};
356static short *current_next_list = NULL; /* when non-zero, nextgroup list */
357static int current_next_flags = 0; /* flags for current_next_list */
358static int current_line_id = 0; /* unique number for current line */
359
360#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
361
362static void syn_sync __ARGS((win_T *wp, linenr_T lnum, synstate_T *last_valid));
363static int syn_match_linecont __ARGS((linenr_T lnum));
364static void syn_start_line __ARGS((void));
365static void syn_update_ends __ARGS((int startofline));
366static void syn_stack_alloc __ARGS((void));
367static int syn_stack_cleanup __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200368static void syn_stack_free_entry __ARGS((synblock_T *block, synstate_T *p));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000369static synstate_T *syn_stack_find_entry __ARGS((linenr_T lnum));
Bram Moolenaardbe31752008-01-13 16:40:19 +0000370static synstate_T *store_current_state __ARGS((void));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000371static void load_current_state __ARGS((synstate_T *from));
372static void invalidate_current_state __ARGS((void));
373static int syn_stack_equal __ARGS((synstate_T *sp));
374static void validate_current_state __ARGS((void));
375static int syn_finish_line __ARGS((int syncing));
Bram Moolenaar56cefaf2008-01-12 15:47:10 +0000376static int syn_current_attr __ARGS((int syncing, int displaying, int *can_spell, int keep_state));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000377static int did_match_already __ARGS((int idx, garray_T *gap));
378static stateitem_T *push_next_match __ARGS((stateitem_T *cur_si));
379static void check_state_ends __ARGS((void));
380static void update_si_attr __ARGS((int idx));
381static void check_keepend __ARGS((void));
382static void update_si_end __ARGS((stateitem_T *sip, int startcol, int force));
383static short *copy_id_list __ARGS((short *list));
384static int in_id_list __ARGS((stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained));
385static int push_current_state __ARGS((int idx));
386static void pop_current_state __ARGS((void));
387
Bram Moolenaar860cae12010-06-05 23:22:07 +0200388static void syn_stack_apply_changes_block __ARGS((synblock_T *block, buf_T *buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000389static 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));
390static void clear_syn_state __ARGS((synstate_T *p));
391static void clear_current_state __ARGS((void));
392
393static void limit_pos __ARGS((lpos_T *pos, lpos_T *limit));
394static void limit_pos_zero __ARGS((lpos_T *pos, lpos_T *limit));
395static void syn_add_end_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
396static void syn_add_start_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
397static char_u *syn_getcurline __ARGS((void));
398static int syn_regexec __ARGS((regmmatch_T *rmp, linenr_T lnum, colnr_T col));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200399static int check_keyword_id __ARGS((char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000400static void syn_cmd_case __ARGS((exarg_T *eap, int syncing));
Bram Moolenaarce0842a2005-07-18 21:58:11 +0000401static void syn_cmd_spell __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000402static void syntax_sync_clear __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200403static void syn_remove_pattern __ARGS((synblock_T *block, int idx));
404static void syn_clear_pattern __ARGS((synblock_T *block, int i));
405static void syn_clear_cluster __ARGS((synblock_T *block, int i));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000406static void syn_cmd_clear __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200407static void syn_cmd_conceal __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000408static void syn_clear_one __ARGS((int id, int syncing));
409static void syn_cmd_on __ARGS((exarg_T *eap, int syncing));
410static void syn_cmd_enable __ARGS((exarg_T *eap, int syncing));
411static void syn_cmd_reset __ARGS((exarg_T *eap, int syncing));
412static void syn_cmd_manual __ARGS((exarg_T *eap, int syncing));
413static void syn_cmd_off __ARGS((exarg_T *eap, int syncing));
414static void syn_cmd_onoff __ARGS((exarg_T *eap, char *name));
415static void syn_cmd_list __ARGS((exarg_T *eap, int syncing));
416static void syn_lines_msg __ARGS((void));
417static void syn_match_msg __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200418static void syn_stack_free_block __ARGS((synblock_T *block));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000419static void syn_list_one __ARGS((int id, int syncing, int link_only));
420static void syn_list_cluster __ARGS((int id));
421static void put_id_list __ARGS((char_u *name, short *list, int attr));
422static void put_pattern __ARGS((char *s, int c, synpat_T *spp, int attr));
Bram Moolenaardad6b692005-01-25 22:14:34 +0000423static int syn_list_keywords __ARGS((int id, hashtab_T *ht, int did_header, int attr));
424static void syn_clear_keyword __ARGS((int id, hashtab_T *ht));
425static void clear_keywtab __ARGS((hashtab_T *ht));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200426static void add_keyword __ARGS((char_u *name, int id, int flags, short *cont_in_list, short *next_list, int conceal_char));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000427static char_u *get_group_name __ARGS((char_u *arg, char_u **name_end));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200428static char_u *get_syn_options __ARGS((char_u *arg, syn_opt_arg_T *opt, int *conceal_char));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000429static void syn_cmd_include __ARGS((exarg_T *eap, int syncing));
430static void syn_cmd_keyword __ARGS((exarg_T *eap, int syncing));
431static void syn_cmd_match __ARGS((exarg_T *eap, int syncing));
432static void syn_cmd_region __ARGS((exarg_T *eap, int syncing));
433#ifdef __BORLANDC__
434static int _RTLENTRYF syn_compare_stub __ARGS((const void *v1, const void *v2));
435#else
436static int syn_compare_stub __ARGS((const void *v1, const void *v2));
437#endif
438static void syn_cmd_cluster __ARGS((exarg_T *eap, int syncing));
439static int syn_scl_name2id __ARGS((char_u *name));
440static int syn_scl_namen2id __ARGS((char_u *linep, int len));
441static int syn_check_cluster __ARGS((char_u *pp, int len));
442static int syn_add_cluster __ARGS((char_u *name));
443static void init_syn_patterns __ARGS((void));
444static char_u *get_syn_pattern __ARGS((char_u *arg, synpat_T *ci));
445static void syn_cmd_sync __ARGS((exarg_T *eap, int syncing));
446static int get_id_list __ARGS((char_u **arg, int keylen, short **list));
447static void syn_combine_list __ARGS((short **clstr1, short **clstr2, int list_op));
448static void syn_incl_toplevel __ARGS((int id, int *flagsp));
449
450/*
451 * Start the syntax recognition for a line. This function is normally called
452 * from the screen updating, once for each displayed line.
453 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
454 * it. Careful: curbuf and curwin are likely to point to another buffer and
455 * window.
456 */
457 void
458syntax_start(wp, lnum)
459 win_T *wp;
460 linenr_T lnum;
461{
462 synstate_T *p;
463 synstate_T *last_valid = NULL;
464 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000465 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000466 linenr_T parsed_lnum;
467 linenr_T first_stored;
468 int dist;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000469 static int changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000470
Bram Moolenaar071d4272004-06-13 20:20:40 +0000471 /*
472 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000473 * Also do this when a change was made, the current state may be invalid
474 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000475 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200476 if (syn_block != wp->w_s || changedtick != syn_buf->b_changedtick)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000477 {
478 invalidate_current_state();
479 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200480 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000481 }
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();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200489 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000490 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200491 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000492
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;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000504 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000505 }
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 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200522 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000523 {
524 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200525 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000526 {
527 if (p->sst_lnum > lnum)
528 break;
529 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
530 {
531 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200532 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000533 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);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200547 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000548 }
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 Moolenaar860cae12010-06-05 23:22:07 +0200556 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000557 dist = 999999;
558 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200559 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000560 while (current_lnum < lnum)
561 {
562 syn_start_line();
563 (void)syn_finish_line(FALSE);
564 ++current_lnum;
565
566 /* If we parsed at least "minlines" lines or started at a valid
567 * state, the current state is considered valid. */
568 if (current_lnum >= first_stored)
569 {
570 /* Check if the saved state entry is for the current line and is
571 * equal to the current state. If so, then validate all saved
572 * states that depended on a change before the parsed line. */
573 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000574 prev = syn_stack_find_entry(current_lnum - 1);
575 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200576 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000577 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000578 sp = prev;
579 while (sp != NULL && sp->sst_lnum < current_lnum)
580 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000581 if (sp != NULL
582 && sp->sst_lnum == current_lnum
583 && syn_stack_equal(sp))
584 {
585 parsed_lnum = current_lnum;
586 prev = sp;
587 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
588 {
589 if (sp->sst_lnum <= lnum)
590 /* valid state before desired line, use this one */
591 prev = sp;
592 else if (sp->sst_change_lnum == 0)
593 /* past saved states depending on change, break here. */
594 break;
595 sp->sst_change_lnum = 0;
596 sp = sp->sst_next;
597 }
598 load_current_state(prev);
599 }
600 /* Store the state at this line when it's the first one, the line
601 * where we start parsing, or some distance from the previously
602 * saved state. But only when parsed at least 'minlines'. */
603 else if (prev == NULL
604 || current_lnum == lnum
605 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000606 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000607 }
608
609 /* This can take a long time: break when CTRL-C pressed. The current
610 * state will be wrong then. */
611 line_breakcheck();
612 if (got_int)
613 {
614 current_lnum = lnum;
615 break;
616 }
617 }
618
619 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000620}
621
622/*
623 * We cannot simply discard growarrays full of state_items or buf_states; we
624 * have to manually release their extmatch pointers first.
625 */
626 static void
627clear_syn_state(p)
628 synstate_T *p;
629{
630 int i;
631 garray_T *gap;
632
633 if (p->sst_stacksize > SST_FIX_STATES)
634 {
635 gap = &(p->sst_union.sst_ga);
636 for (i = 0; i < gap->ga_len; i++)
637 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
638 ga_clear(gap);
639 }
640 else
641 {
642 for (i = 0; i < p->sst_stacksize; i++)
643 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
644 }
645}
646
647/*
648 * Cleanup the current_state stack.
649 */
650 static void
651clear_current_state()
652{
653 int i;
654 stateitem_T *sip;
655
656 sip = (stateitem_T *)(current_state.ga_data);
657 for (i = 0; i < current_state.ga_len; i++)
658 unref_extmatch(sip[i].si_extmatch);
659 ga_clear(&current_state);
660}
661
662/*
663 * Try to find a synchronisation point for line "lnum".
664 *
665 * This sets current_lnum and the current state. One of three methods is
666 * used:
667 * 1. Search backwards for the end of a C-comment.
668 * 2. Search backwards for given sync patterns.
669 * 3. Simply start on a given number of lines above "lnum".
670 */
671 static void
672syn_sync(wp, start_lnum, last_valid)
673 win_T *wp;
674 linenr_T start_lnum;
675 synstate_T *last_valid;
676{
677 buf_T *curbuf_save;
678 win_T *curwin_save;
679 pos_T cursor_save;
680 int idx;
681 linenr_T lnum;
682 linenr_T end_lnum;
683 linenr_T break_lnum;
684 int had_sync_point;
685 stateitem_T *cur_si;
686 synpat_T *spp;
687 char_u *line;
688 int found_flags = 0;
689 int found_match_idx = 0;
690 linenr_T found_current_lnum = 0;
691 int found_current_col= 0;
692 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000693 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000694
695 /*
696 * Clear any current state that might be hanging around.
697 */
698 invalidate_current_state();
699
700 /*
701 * Start at least "minlines" back. Default starting point for parsing is
702 * there.
703 * Start further back, to avoid that scrolling backwards will result in
704 * resyncing for every line. Now it resyncs only one out of N lines,
705 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
706 * Watch out for overflow when minlines is MAXLNUM.
707 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200708 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000709 start_lnum = 1;
710 else
711 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200712 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000713 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200714 else if (syn_block->b_syn_sync_minlines < 10)
715 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000716 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200717 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
718 if (syn_block->b_syn_sync_maxlines != 0
719 && lnum > syn_block->b_syn_sync_maxlines)
720 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000721 if (lnum >= start_lnum)
722 start_lnum = 1;
723 else
724 start_lnum -= lnum;
725 }
726 current_lnum = start_lnum;
727
728 /*
729 * 1. Search backwards for the end of a C-style comment.
730 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200731 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000732 {
733 /* Need to make syn_buf the current buffer for a moment, to be able to
734 * use find_start_comment(). */
735 curwin_save = curwin;
736 curwin = wp;
737 curbuf_save = curbuf;
738 curbuf = syn_buf;
739
740 /*
741 * Skip lines that end in a backslash.
742 */
743 for ( ; start_lnum > 1; --start_lnum)
744 {
745 line = ml_get(start_lnum - 1);
746 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
747 break;
748 }
749 current_lnum = start_lnum;
750
751 /* set cursor to start of search */
752 cursor_save = wp->w_cursor;
753 wp->w_cursor.lnum = start_lnum;
754 wp->w_cursor.col = 0;
755
756 /*
757 * If the line is inside a comment, need to find the syntax item that
758 * defines the comment.
759 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
760 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200761 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000762 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200763 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
764 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
765 == syn_block->b_syn_sync_id
766 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000767 {
768 validate_current_state();
769 if (push_current_state(idx) == OK)
770 update_si_attr(current_state.ga_len - 1);
771 break;
772 }
773 }
774
775 /* restore cursor and buffer */
776 wp->w_cursor = cursor_save;
777 curwin = curwin_save;
778 curbuf = curbuf_save;
779 }
780
781 /*
782 * 2. Search backwards for given sync patterns.
783 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200784 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000785 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200786 if (syn_block->b_syn_sync_maxlines != 0
787 && start_lnum > syn_block->b_syn_sync_maxlines)
788 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000789 else
790 break_lnum = 0;
791
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000792 found_m_endpos.lnum = 0;
793 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000794 end_lnum = start_lnum;
795 lnum = start_lnum;
796 while (--lnum > break_lnum)
797 {
798 /* This can take a long time: break when CTRL-C pressed. */
799 line_breakcheck();
800 if (got_int)
801 {
802 invalidate_current_state();
803 current_lnum = start_lnum;
804 break;
805 }
806
807 /* Check if we have run into a valid saved state stack now. */
808 if (last_valid != NULL && lnum == last_valid->sst_lnum)
809 {
810 load_current_state(last_valid);
811 break;
812 }
813
814 /*
815 * Check if the previous line has the line-continuation pattern.
816 */
817 if (lnum > 1 && syn_match_linecont(lnum - 1))
818 continue;
819
820 /*
821 * Start with nothing on the state stack
822 */
823 validate_current_state();
824
825 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
826 {
827 syn_start_line();
828 for (;;)
829 {
830 had_sync_point = syn_finish_line(TRUE);
831 /*
832 * When a sync point has been found, remember where, and
833 * continue to look for another one, further on in the line.
834 */
835 if (had_sync_point && current_state.ga_len)
836 {
837 cur_si = &CUR_STATE(current_state.ga_len - 1);
838 if (cur_si->si_m_endpos.lnum > start_lnum)
839 {
840 /* ignore match that goes to after where started */
841 current_lnum = end_lnum;
842 break;
843 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000844 if (cur_si->si_idx < 0)
845 {
846 /* Cannot happen? */
847 found_flags = 0;
848 found_match_idx = KEYWORD_IDX;
849 }
850 else
851 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200852 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000853 found_flags = spp->sp_flags;
854 found_match_idx = spp->sp_sync_idx;
855 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000856 found_current_lnum = current_lnum;
857 found_current_col = current_col;
858 found_m_endpos = cur_si->si_m_endpos;
859 /*
860 * Continue after the match (be aware of a zero-length
861 * match).
862 */
863 if (found_m_endpos.lnum > current_lnum)
864 {
865 current_lnum = found_m_endpos.lnum;
866 current_col = found_m_endpos.col;
867 if (current_lnum >= end_lnum)
868 break;
869 }
870 else if (found_m_endpos.col > current_col)
871 current_col = found_m_endpos.col;
872 else
873 ++current_col;
874
875 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000876 * an item that ends here, need to do that now. Be
877 * careful not to go past the NUL. */
878 prev_current_col = current_col;
879 if (syn_getcurline()[current_col] != NUL)
880 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000881 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000882 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000883 }
884 else
885 break;
886 }
887 }
888
889 /*
890 * If a sync point was encountered, break here.
891 */
892 if (found_flags)
893 {
894 /*
895 * Put the item that was specified by the sync point on the
896 * state stack. If there was no item specified, make the
897 * state stack empty.
898 */
899 clear_current_state();
900 if (found_match_idx >= 0
901 && push_current_state(found_match_idx) == OK)
902 update_si_attr(current_state.ga_len - 1);
903
904 /*
905 * When using "grouphere", continue from the sync point
906 * match, until the end of the line. Parsing starts at
907 * the next line.
908 * For "groupthere" the parsing starts at start_lnum.
909 */
910 if (found_flags & HL_SYNC_HERE)
911 {
912 if (current_state.ga_len)
913 {
914 cur_si = &CUR_STATE(current_state.ga_len - 1);
915 cur_si->si_h_startpos.lnum = found_current_lnum;
916 cur_si->si_h_startpos.col = found_current_col;
917 update_si_end(cur_si, (int)current_col, TRUE);
918 check_keepend();
919 }
920 current_col = found_m_endpos.col;
921 current_lnum = found_m_endpos.lnum;
922 (void)syn_finish_line(FALSE);
923 ++current_lnum;
924 }
925 else
926 current_lnum = start_lnum;
927
928 break;
929 }
930
931 end_lnum = lnum;
932 invalidate_current_state();
933 }
934
935 /* Ran into start of the file or exceeded maximum number of lines */
936 if (lnum <= break_lnum)
937 {
938 invalidate_current_state();
939 current_lnum = break_lnum + 1;
940 }
941 }
942
943 validate_current_state();
944}
945
946/*
947 * Return TRUE if the line-continuation pattern matches in line "lnum".
948 */
949 static int
950syn_match_linecont(lnum)
951 linenr_T lnum;
952{
953 regmmatch_T regmatch;
954
Bram Moolenaar860cae12010-06-05 23:22:07 +0200955 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000956 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200957 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
958 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000959 return syn_regexec(&regmatch, lnum, (colnr_T)0);
960 }
961 return FALSE;
962}
963
964/*
965 * Prepare the current state for the start of a line.
966 */
967 static void
968syn_start_line()
969{
970 current_finished = FALSE;
971 current_col = 0;
972
973 /*
974 * Need to update the end of a start/skip/end that continues from the
975 * previous line and regions that have "keepend".
976 */
977 if (current_state.ga_len > 0)
978 syn_update_ends(TRUE);
979
980 next_match_idx = -1;
981 ++current_line_id;
982}
983
984/*
985 * Check for items in the stack that need their end updated.
986 * When "startofline" is TRUE the last item is always updated.
987 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
988 */
989 static void
990syn_update_ends(startofline)
991 int startofline;
992{
993 stateitem_T *cur_si;
994 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000995 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000996
997 if (startofline)
998 {
999 /* Check for a match carried over from a previous line with a
1000 * contained region. The match ends as soon as the region ends. */
1001 for (i = 0; i < current_state.ga_len; ++i)
1002 {
1003 cur_si = &CUR_STATE(i);
1004 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001005 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001006 == SPTYPE_MATCH
1007 && cur_si->si_m_endpos.lnum < current_lnum)
1008 {
1009 cur_si->si_flags |= HL_MATCHCONT;
1010 cur_si->si_m_endpos.lnum = 0;
1011 cur_si->si_m_endpos.col = 0;
1012 cur_si->si_h_endpos = cur_si->si_m_endpos;
1013 cur_si->si_ends = TRUE;
1014 }
1015 }
1016 }
1017
1018 /*
1019 * Need to update the end of a start/skip/end that continues from the
1020 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001021 * influence contained items. If we've just removed "extend"
1022 * (startofline == 0) then we should update ends of normal regions
1023 * contained inside "keepend" because "extend" could have extended
1024 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001025 * Then check for items ending in column 0.
1026 */
1027 i = current_state.ga_len - 1;
1028 if (keepend_level >= 0)
1029 for ( ; i > keepend_level; --i)
1030 if (CUR_STATE(i).si_flags & HL_EXTEND)
1031 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001032
1033 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001034 for ( ; i < current_state.ga_len; ++i)
1035 {
1036 cur_si = &CUR_STATE(i);
1037 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001038 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001039 || (i == current_state.ga_len - 1 && startofline))
1040 {
1041 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1042 cur_si->si_h_startpos.lnum = current_lnum;
1043
1044 if (!(cur_si->si_flags & HL_MATCHCONT))
1045 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001046
1047 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1048 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001049 }
1050 }
1051 check_keepend();
1052 check_state_ends();
1053}
1054
1055/****************************************
1056 * Handling of the state stack cache.
1057 */
1058
1059/*
1060 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1061 *
1062 * To speed up syntax highlighting, the state stack for the start of some
1063 * lines is cached. These entries can be used to start parsing at that point.
1064 *
1065 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1066 * valid entries. b_sst_first points to the first one, then follow sst_next.
1067 * The entries are sorted on line number. The first entry is often for line 2
1068 * (line 1 always starts with an empty stack).
1069 * There is also a list for free entries. This construction is used to avoid
1070 * having to allocate and free memory blocks too often.
1071 *
1072 * When making changes to the buffer, this is logged in b_mod_*. When calling
1073 * update_screen() to update the display, it will call
1074 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1075 * entries. The entries which are inside the changed area are removed,
1076 * because they must be recomputed. Entries below the changed have their line
1077 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1078 * set to indicate that a check must be made if the changed lines would change
1079 * the cached entry.
1080 *
1081 * When later displaying lines, an entry is stored for each line. Displayed
1082 * lines are likely to be displayed again, in which case the state at the
1083 * start of the line is needed.
1084 * For not displayed lines, an entry is stored for every so many lines. These
1085 * entries will be used e.g., when scrolling backwards. The distance between
1086 * entries depends on the number of lines in the buffer. For small buffers
1087 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1088 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1089 */
1090
Bram Moolenaar860cae12010-06-05 23:22:07 +02001091 static void
1092syn_stack_free_block(block)
1093 synblock_T *block;
1094{
1095 synstate_T *p;
1096
1097 if (block->b_sst_array != NULL)
1098 {
1099 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1100 clear_syn_state(p);
1101 vim_free(block->b_sst_array);
1102 block->b_sst_array = NULL;
1103 block->b_sst_len = 0;
1104 }
1105}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001106/*
1107 * Free b_sst_array[] for buffer "buf".
1108 * Used when syntax items changed to force resyncing everywhere.
1109 */
1110 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001111syn_stack_free_all(block)
1112 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001113{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001114 win_T *wp;
1115
Bram Moolenaar860cae12010-06-05 23:22:07 +02001116 syn_stack_free_block(block);
1117
1118
Bram Moolenaar071d4272004-06-13 20:20:40 +00001119#ifdef FEAT_FOLDING
1120 /* When using "syntax" fold method, must update all folds. */
1121 FOR_ALL_WINDOWS(wp)
1122 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001123 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001124 foldUpdateAll(wp);
1125 }
1126#endif
1127}
1128
1129/*
1130 * Allocate the syntax state stack for syn_buf when needed.
1131 * If the number of entries in b_sst_array[] is much too big or a bit too
1132 * small, reallocate it.
1133 * Also used to allocate b_sst_array[] for the first time.
1134 */
1135 static void
1136syn_stack_alloc()
1137{
1138 long len;
1139 synstate_T *to, *from;
1140 synstate_T *sstp;
1141
1142 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1143 if (len < SST_MIN_ENTRIES)
1144 len = SST_MIN_ENTRIES;
1145 else if (len > SST_MAX_ENTRIES)
1146 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001147 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001148 {
1149 /* Allocate 50% too much, to avoid reallocating too often. */
1150 len = syn_buf->b_ml.ml_line_count;
1151 len = (len + len / 2) / SST_DIST + Rows * 2;
1152 if (len < SST_MIN_ENTRIES)
1153 len = SST_MIN_ENTRIES;
1154 else if (len > SST_MAX_ENTRIES)
1155 len = SST_MAX_ENTRIES;
1156
Bram Moolenaar860cae12010-06-05 23:22:07 +02001157 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001158 {
1159 /* When shrinking the array, cleanup the existing stack.
1160 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001161 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001162 && syn_stack_cleanup())
1163 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001164 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1165 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001166 }
1167
1168 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1169 if (sstp == NULL) /* out of memory! */
1170 return;
1171
1172 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001173 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001174 {
1175 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001176 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001177 from = from->sst_next)
1178 {
1179 ++to;
1180 *to = *from;
1181 to->sst_next = to + 1;
1182 }
1183 }
1184 if (to != sstp - 1)
1185 {
1186 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001187 syn_block->b_sst_first = sstp;
1188 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001189 }
1190 else
1191 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001192 syn_block->b_sst_first = NULL;
1193 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001194 }
1195
1196 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001197 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001198 while (++to < sstp + len)
1199 to->sst_next = to + 1;
1200 (sstp + len - 1)->sst_next = NULL;
1201
Bram Moolenaar860cae12010-06-05 23:22:07 +02001202 vim_free(syn_block->b_sst_array);
1203 syn_block->b_sst_array = sstp;
1204 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001205 }
1206}
1207
1208/*
1209 * Check for changes in a buffer to affect stored syntax states. Uses the
1210 * b_mod_* fields.
1211 * Called from update_screen(), before screen is being updated, once for each
1212 * displayed buffer.
1213 */
1214 void
1215syn_stack_apply_changes(buf)
1216 buf_T *buf;
1217{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001218 win_T *wp;
1219
1220 syn_stack_apply_changes_block(&buf->b_s, buf);
1221
1222 FOR_ALL_WINDOWS(wp)
1223 {
1224 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1225 syn_stack_apply_changes_block(wp->w_s, buf);
1226 }
1227}
1228
1229 static void
1230syn_stack_apply_changes_block(block, buf)
1231 synblock_T *block;
1232 buf_T *buf;
1233{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001234 synstate_T *p, *prev, *np;
1235 linenr_T n;
1236
Bram Moolenaar860cae12010-06-05 23:22:07 +02001237 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001238 return;
1239
1240 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001241 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001242 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001243 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244 {
1245 n = p->sst_lnum + buf->b_mod_xlines;
1246 if (n <= buf->b_mod_bot)
1247 {
1248 /* this state is inside the changed area, remove it */
1249 np = p->sst_next;
1250 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001251 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001252 else
1253 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001254 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001255 p = np;
1256 continue;
1257 }
1258 /* This state is below the changed area. Remember the line
1259 * that needs to be parsed before this entry can be made valid
1260 * again. */
1261 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1262 {
1263 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1264 p->sst_change_lnum += buf->b_mod_xlines;
1265 else
1266 p->sst_change_lnum = buf->b_mod_top;
1267 }
1268 if (p->sst_change_lnum == 0
1269 || p->sst_change_lnum < buf->b_mod_bot)
1270 p->sst_change_lnum = buf->b_mod_bot;
1271
1272 p->sst_lnum = n;
1273 }
1274 prev = p;
1275 p = p->sst_next;
1276 }
1277}
1278
1279/*
1280 * Reduce the number of entries in the state stack for syn_buf.
1281 * Returns TRUE if at least one entry was freed.
1282 */
1283 static int
1284syn_stack_cleanup()
1285{
1286 synstate_T *p, *prev;
1287 disptick_T tick;
1288 int above;
1289 int dist;
1290 int retval = FALSE;
1291
Bram Moolenaar860cae12010-06-05 23:22:07 +02001292 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001293 return retval;
1294
1295 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001296 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001297 dist = 999999;
1298 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001299 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001300
1301 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001302 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001303 * be removed. Set "above" when the "tick" for the oldest entry is above
1304 * "b_sst_lasttick" (the display tick wraps around).
1305 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001306 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001307 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001308 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001309 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1310 {
1311 if (prev->sst_lnum + dist > p->sst_lnum)
1312 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001313 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001314 {
1315 if (!above || p->sst_tick < tick)
1316 tick = p->sst_tick;
1317 above = TRUE;
1318 }
1319 else if (!above && p->sst_tick < tick)
1320 tick = p->sst_tick;
1321 }
1322 }
1323
1324 /*
1325 * Go through the list to make the entries for the oldest tick at an
1326 * interval of several lines.
1327 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001328 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001329 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1330 {
1331 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1332 {
1333 /* Move this entry from used list to free list */
1334 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001335 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001336 p = prev;
1337 retval = TRUE;
1338 }
1339 }
1340 return retval;
1341}
1342
1343/*
1344 * Free the allocated memory for a syn_state item.
1345 * Move the entry into the free list.
1346 */
1347 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001348syn_stack_free_entry(block, p)
1349 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001350 synstate_T *p;
1351{
1352 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001353 p->sst_next = block->b_sst_firstfree;
1354 block->b_sst_firstfree = p;
1355 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001356}
1357
1358/*
1359 * Find an entry in the list of state stacks at or before "lnum".
1360 * Returns NULL when there is no entry or the first entry is after "lnum".
1361 */
1362 static synstate_T *
1363syn_stack_find_entry(lnum)
1364 linenr_T lnum;
1365{
1366 synstate_T *p, *prev;
1367
1368 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001369 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001370 {
1371 if (p->sst_lnum == lnum)
1372 return p;
1373 if (p->sst_lnum > lnum)
1374 break;
1375 }
1376 return prev;
1377}
1378
1379/*
1380 * Try saving the current state in b_sst_array[].
1381 * The current state must be valid for the start of the current_lnum line!
1382 */
1383 static synstate_T *
Bram Moolenaardbe31752008-01-13 16:40:19 +00001384store_current_state()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001385{
1386 int i;
1387 synstate_T *p;
1388 bufstate_T *bp;
1389 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001390 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001391
1392 /*
1393 * If the current state contains a start or end pattern that continues
1394 * from the previous line, we can't use it. Don't store it then.
1395 */
1396 for (i = current_state.ga_len - 1; i >= 0; --i)
1397 {
1398 cur_si = &CUR_STATE(i);
1399 if (cur_si->si_h_startpos.lnum >= current_lnum
1400 || cur_si->si_m_endpos.lnum >= current_lnum
1401 || cur_si->si_h_endpos.lnum >= current_lnum
1402 || (cur_si->si_end_idx
1403 && cur_si->si_eoe_pos.lnum >= current_lnum))
1404 break;
1405 }
1406 if (i >= 0)
1407 {
1408 if (sp != NULL)
1409 {
1410 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001411 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001412 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001413 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001414 else
1415 {
1416 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001417 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001418 if (p->sst_next == sp)
1419 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001420 if (p != NULL) /* just in case */
1421 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001422 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001423 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001424 sp = NULL;
1425 }
1426 }
1427 else if (sp == NULL || sp->sst_lnum != current_lnum)
1428 {
1429 /*
1430 * Add a new entry
1431 */
1432 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001433 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001434 {
1435 (void)syn_stack_cleanup();
1436 /* "sp" may have been moved to the freelist now */
1437 sp = syn_stack_find_entry(current_lnum);
1438 }
1439 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001440 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001441 sp = NULL;
1442 else
1443 {
1444 /* Take the first item from the free list and put it in the used
1445 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001446 p = syn_block->b_sst_firstfree;
1447 syn_block->b_sst_firstfree = p->sst_next;
1448 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001449 if (sp == NULL)
1450 {
1451 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001452 p->sst_next = syn_block->b_sst_first;
1453 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001454 }
1455 else
1456 {
1457 /* insert in list after *sp */
1458 p->sst_next = sp->sst_next;
1459 sp->sst_next = p;
1460 }
1461 sp = p;
1462 sp->sst_stacksize = 0;
1463 sp->sst_lnum = current_lnum;
1464 }
1465 }
1466 if (sp != NULL)
1467 {
1468 /* When overwriting an existing state stack, clear it first */
1469 clear_syn_state(sp);
1470 sp->sst_stacksize = current_state.ga_len;
1471 if (current_state.ga_len > SST_FIX_STATES)
1472 {
1473 /* Need to clear it, might be something remaining from when the
1474 * length was less than SST_FIX_STATES. */
1475 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1476 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1477 sp->sst_stacksize = 0;
1478 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001479 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001480 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1481 }
1482 else
1483 bp = sp->sst_union.sst_stack;
1484 for (i = 0; i < sp->sst_stacksize; ++i)
1485 {
1486 bp[i].bs_idx = CUR_STATE(i).si_idx;
1487 bp[i].bs_flags = CUR_STATE(i).si_flags;
1488 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1489 }
1490 sp->sst_next_flags = current_next_flags;
1491 sp->sst_next_list = current_next_list;
1492 sp->sst_tick = display_tick;
1493 sp->sst_change_lnum = 0;
1494 }
1495 current_state_stored = TRUE;
1496 return sp;
1497}
1498
1499/*
1500 * Copy a state stack from "from" in b_sst_array[] to current_state;
1501 */
1502 static void
1503load_current_state(from)
1504 synstate_T *from;
1505{
1506 int i;
1507 bufstate_T *bp;
1508
1509 clear_current_state();
1510 validate_current_state();
1511 keepend_level = -1;
1512 if (from->sst_stacksize
1513 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1514 {
1515 if (from->sst_stacksize > SST_FIX_STATES)
1516 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1517 else
1518 bp = from->sst_union.sst_stack;
1519 for (i = 0; i < from->sst_stacksize; ++i)
1520 {
1521 CUR_STATE(i).si_idx = bp[i].bs_idx;
1522 CUR_STATE(i).si_flags = bp[i].bs_flags;
1523 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1524 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1525 keepend_level = i;
1526 CUR_STATE(i).si_ends = FALSE;
1527 CUR_STATE(i).si_m_lnum = 0;
1528 if (CUR_STATE(i).si_idx >= 0)
1529 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001530 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001531 else
1532 CUR_STATE(i).si_next_list = NULL;
1533 update_si_attr(i);
1534 }
1535 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001536 }
1537 current_next_list = from->sst_next_list;
1538 current_next_flags = from->sst_next_flags;
1539 current_lnum = from->sst_lnum;
1540}
1541
1542/*
1543 * Compare saved state stack "*sp" with the current state.
1544 * Return TRUE when they are equal.
1545 */
1546 static int
1547syn_stack_equal(sp)
1548 synstate_T *sp;
1549{
1550 int i, j;
1551 bufstate_T *bp;
1552 reg_extmatch_T *six, *bsx;
1553
1554 /* First a quick check if the stacks have the same size end nextlist. */
1555 if (sp->sst_stacksize == current_state.ga_len
1556 && sp->sst_next_list == current_next_list)
1557 {
1558 /* Need to compare all states on both stacks. */
1559 if (sp->sst_stacksize > SST_FIX_STATES)
1560 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1561 else
1562 bp = sp->sst_union.sst_stack;
1563
1564 for (i = current_state.ga_len; --i >= 0; )
1565 {
1566 /* If the item has another index the state is different. */
1567 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1568 break;
1569 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1570 {
1571 /* When the extmatch pointers are different, the strings in
1572 * them can still be the same. Check if the extmatch
1573 * references are equal. */
1574 bsx = bp[i].bs_extmatch;
1575 six = CUR_STATE(i).si_extmatch;
1576 /* If one of the extmatch pointers is NULL the states are
1577 * different. */
1578 if (bsx == NULL || six == NULL)
1579 break;
1580 for (j = 0; j < NSUBEXP; ++j)
1581 {
1582 /* Check each referenced match string. They must all be
1583 * equal. */
1584 if (bsx->matches[j] != six->matches[j])
1585 {
1586 /* If the pointer is different it can still be the
1587 * same text. Compare the strings, ignore case when
1588 * the start item has the sp_ic flag set. */
1589 if (bsx->matches[j] == NULL
1590 || six->matches[j] == NULL)
1591 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001592 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001593 ? MB_STRICMP(bsx->matches[j],
1594 six->matches[j]) != 0
1595 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1596 break;
1597 }
1598 }
1599 if (j != NSUBEXP)
1600 break;
1601 }
1602 }
1603 if (i < 0)
1604 return TRUE;
1605 }
1606 return FALSE;
1607}
1608
1609/*
1610 * We stop parsing syntax above line "lnum". If the stored state at or below
1611 * this line depended on a change before it, it now depends on the line below
1612 * the last parsed line.
1613 * The window looks like this:
1614 * line which changed
1615 * displayed line
1616 * displayed line
1617 * lnum -> line below window
1618 */
1619 void
1620syntax_end_parsing(lnum)
1621 linenr_T lnum;
1622{
1623 synstate_T *sp;
1624
1625 sp = syn_stack_find_entry(lnum);
1626 if (sp != NULL && sp->sst_lnum < lnum)
1627 sp = sp->sst_next;
1628
1629 if (sp != NULL && sp->sst_change_lnum != 0)
1630 sp->sst_change_lnum = lnum;
1631}
1632
1633/*
1634 * End of handling of the state stack.
1635 ****************************************/
1636
1637 static void
1638invalidate_current_state()
1639{
1640 clear_current_state();
1641 current_state.ga_itemsize = 0; /* mark current_state invalid */
1642 current_next_list = NULL;
1643 keepend_level = -1;
1644}
1645
1646 static void
1647validate_current_state()
1648{
1649 current_state.ga_itemsize = sizeof(stateitem_T);
1650 current_state.ga_growsize = 3;
1651}
1652
1653/*
1654 * Return TRUE if the syntax at start of lnum changed since last time.
1655 * This will only be called just after get_syntax_attr() for the previous
1656 * line, to check if the next line needs to be redrawn too.
1657 */
1658 int
1659syntax_check_changed(lnum)
1660 linenr_T lnum;
1661{
1662 int retval = TRUE;
1663 synstate_T *sp;
1664
Bram Moolenaar071d4272004-06-13 20:20:40 +00001665 /*
1666 * Check the state stack when:
1667 * - lnum is just below the previously syntaxed line.
1668 * - lnum is not before the lines with saved states.
1669 * - lnum is not past the lines with saved states.
1670 * - lnum is at or before the last changed line.
1671 */
1672 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1673 {
1674 sp = syn_stack_find_entry(lnum);
1675 if (sp != NULL && sp->sst_lnum == lnum)
1676 {
1677 /*
1678 * finish the previous line (needed when not all of the line was
1679 * drawn)
1680 */
1681 (void)syn_finish_line(FALSE);
1682
1683 /*
1684 * Compare the current state with the previously saved state of
1685 * the line.
1686 */
1687 if (syn_stack_equal(sp))
1688 retval = FALSE;
1689
1690 /*
1691 * Store the current state in b_sst_array[] for later use.
1692 */
1693 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001694 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001695 }
1696 }
1697
Bram Moolenaar071d4272004-06-13 20:20:40 +00001698 return retval;
1699}
1700
1701/*
1702 * Finish the current line.
1703 * This doesn't return any attributes, it only gets the state at the end of
1704 * the line. It can start anywhere in the line, as long as the current state
1705 * is valid.
1706 */
1707 static int
1708syn_finish_line(syncing)
1709 int syncing; /* called for syncing */
1710{
1711 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001712 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001713
1714 if (!current_finished)
1715 {
1716 while (!current_finished)
1717 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001718 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001719 /*
1720 * When syncing, and found some item, need to check the item.
1721 */
1722 if (syncing && current_state.ga_len)
1723 {
1724 /*
1725 * Check for match with sync item.
1726 */
1727 cur_si = &CUR_STATE(current_state.ga_len - 1);
1728 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001729 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001730 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1731 return TRUE;
1732
1733 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001734 * that ends here, need to do that now. Be careful not to go
1735 * past the NUL. */
1736 prev_current_col = current_col;
1737 if (syn_getcurline()[current_col] != NUL)
1738 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001739 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001740 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001741 }
1742 ++current_col;
1743 }
1744 }
1745 return FALSE;
1746}
1747
1748/*
1749 * Return highlight attributes for next character.
1750 * Must first call syntax_start() once for the line.
1751 * "col" is normally 0 for the first use in a line, and increments by one each
1752 * time. It's allowed to skip characters and to stop before the end of the
1753 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001754 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1755 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001756 */
1757 int
Bram Moolenaar860cae12010-06-05 23:22:07 +02001758get_syntax_attr(col, p_flags, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001759 colnr_T col;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001760 int *p_flags UNUSED;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001761 int *can_spell;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001762 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001763{
1764 int attr = 0;
1765
Bram Moolenaar349955a2007-08-14 21:07:36 +00001766 if (can_spell != NULL)
1767 /* Default: Only do spelling when there is no @Spell cluster or when
1768 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001769 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1770 ? (syn_block->b_spell_cluster_id == 0)
1771 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001772
Bram Moolenaar071d4272004-06-13 20:20:40 +00001773 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001774 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001775 return 0;
1776
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001777 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001778 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001779 {
1780 clear_current_state();
1781#ifdef FEAT_EVAL
1782 current_id = 0;
1783 current_trans_id = 0;
1784#endif
1785 return 0;
1786 }
1787
Bram Moolenaar071d4272004-06-13 20:20:40 +00001788 /* Make sure current_state is valid */
1789 if (INVALID_STATE(&current_state))
1790 validate_current_state();
1791
1792 /*
1793 * Skip from the current column to "col", get the attributes for "col".
1794 */
1795 while (current_col <= col)
1796 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001797 attr = syn_current_attr(FALSE, TRUE, can_spell,
1798 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001799 ++current_col;
1800 }
1801
Bram Moolenaar860cae12010-06-05 23:22:07 +02001802#ifdef FEAT_CONCEAL
1803 if (p_flags != NULL)
1804 *p_flags = current_flags;
1805#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001806 return attr;
1807}
1808
1809/*
1810 * Get syntax attributes for current_lnum, current_col.
1811 */
1812 static int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001813syn_current_attr(syncing, displaying, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001814 int syncing; /* When 1: called for syncing */
1815 int displaying; /* result will be displayed */
Bram Moolenaar217ad922005-03-20 22:37:15 +00001816 int *can_spell; /* return: do spell checking */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001817 int keep_state; /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001818{
1819 int syn_id;
1820 lpos_T endpos; /* was: char_u *endp; */
1821 lpos_T hl_startpos; /* was: int hl_startcol; */
1822 lpos_T hl_endpos;
1823 lpos_T eos_pos; /* end-of-start match (start region) */
1824 lpos_T eoe_pos; /* end-of-end pattern */
1825 int end_idx; /* group ID for end pattern */
1826 int idx;
1827 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001828 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001829 int startcol;
1830 int endcol;
1831 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001832 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001833 short *next_list;
1834 int found_match; /* found usable match */
1835 static int try_next_column = FALSE; /* must try in next col */
1836 int do_keywords;
1837 regmmatch_T regmatch;
1838 lpos_T pos;
1839 int lc_col;
1840 reg_extmatch_T *cur_extmatch = NULL;
1841 char_u *line; /* current line. NOTE: becomes invalid after
1842 looking for a pattern match! */
1843
1844 /* variables for zero-width matches that have a "nextgroup" argument */
1845 int keep_next_list;
1846 int zero_width_next_list = FALSE;
1847 garray_T zero_width_next_ga;
1848
1849 /*
1850 * No character, no attributes! Past end of line?
1851 * Do try matching with an empty line (could be the start of a region).
1852 */
1853 line = syn_getcurline();
1854 if (line[current_col] == NUL && current_col != 0)
1855 {
1856 /*
1857 * If we found a match after the last column, use it.
1858 */
1859 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1860 && next_match_col != MAXCOL)
1861 (void)push_next_match(NULL);
1862
1863 current_finished = TRUE;
1864 current_state_stored = FALSE;
1865 return 0;
1866 }
1867
1868 /* if the current or next character is NUL, we will finish the line now */
1869 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1870 {
1871 current_finished = TRUE;
1872 current_state_stored = FALSE;
1873 }
1874
1875 /*
1876 * When in the previous column there was a match but it could not be used
1877 * (empty match or already matched in this column) need to try again in
1878 * the next column.
1879 */
1880 if (try_next_column)
1881 {
1882 next_match_idx = -1;
1883 try_next_column = FALSE;
1884 }
1885
1886 /* Only check for keywords when not syncing and there are some. */
1887 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001888 && (syn_block->b_keywtab.ht_used > 0
1889 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001890
1891 /* Init the list of zero-width matches with a nextlist. This is used to
1892 * avoid matching the same item in the same position twice. */
1893 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1894
1895 /*
1896 * Repeat matching keywords and patterns, to find contained items at the
1897 * same column. This stops when there are no extra matches at the current
1898 * column.
1899 */
1900 do
1901 {
1902 found_match = FALSE;
1903 keep_next_list = FALSE;
1904 syn_id = 0;
1905
1906 /*
1907 * 1. Check for a current state.
1908 * Only when there is no current state, or if the current state may
1909 * contain other things, we need to check for keywords and patterns.
1910 * Always need to check for contained items if some item has the
1911 * "containedin" argument (takes extra time!).
1912 */
1913 if (current_state.ga_len)
1914 cur_si = &CUR_STATE(current_state.ga_len - 1);
1915 else
1916 cur_si = NULL;
1917
Bram Moolenaar860cae12010-06-05 23:22:07 +02001918 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001919 || cur_si->si_cont_list != NULL)
1920 {
1921 /*
1922 * 2. Check for keywords, if on a keyword char after a non-keyword
1923 * char. Don't do this when syncing.
1924 */
1925 if (do_keywords)
1926 {
1927 line = syn_getcurline();
1928 if (vim_iswordc_buf(line + current_col, syn_buf)
1929 && (current_col == 0
1930 || !vim_iswordc_buf(line + current_col - 1
1931#ifdef FEAT_MBYTE
1932 - (has_mbyte
1933 ? (*mb_head_off)(line, line + current_col - 1)
1934 : 0)
1935#endif
1936 , syn_buf)))
1937 {
1938 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001939 &endcol, &flags, &next_list, cur_si,
1940 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001941 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001942 {
1943 if (push_current_state(KEYWORD_IDX) == OK)
1944 {
1945 cur_si = &CUR_STATE(current_state.ga_len - 1);
1946 cur_si->si_m_startcol = current_col;
1947 cur_si->si_h_startpos.lnum = current_lnum;
1948 cur_si->si_h_startpos.col = 0; /* starts right away */
1949 cur_si->si_m_endpos.lnum = current_lnum;
1950 cur_si->si_m_endpos.col = endcol;
1951 cur_si->si_h_endpos.lnum = current_lnum;
1952 cur_si->si_h_endpos.col = endcol;
1953 cur_si->si_ends = TRUE;
1954 cur_si->si_end_idx = 0;
1955 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001956#ifdef FEAT_CONCEAL
1957 cur_si->si_char = cchar;
1958 if (current_state.ga_len > 1)
1959 cur_si->si_flags |=
1960 CUR_STATE(current_state.ga_len - 2).si_flags
1961 & HL_CONCEAL;
1962#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001963 cur_si->si_id = syn_id;
1964 cur_si->si_trans_id = syn_id;
1965 if (flags & HL_TRANSP)
1966 {
1967 if (current_state.ga_len < 2)
1968 {
1969 cur_si->si_attr = 0;
1970 cur_si->si_trans_id = 0;
1971 }
1972 else
1973 {
1974 cur_si->si_attr = CUR_STATE(
1975 current_state.ga_len - 2).si_attr;
1976 cur_si->si_trans_id = CUR_STATE(
1977 current_state.ga_len - 2).si_trans_id;
1978 }
1979 }
1980 else
1981 cur_si->si_attr = syn_id2attr(syn_id);
1982 cur_si->si_cont_list = NULL;
1983 cur_si->si_next_list = next_list;
1984 check_keepend();
1985 }
1986 else
1987 vim_free(next_list);
1988 }
1989 }
1990 }
1991
1992 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001993 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001994 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001995 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001996 {
1997 /*
1998 * If we didn't check for a match yet, or we are past it, check
1999 * for any match with a pattern.
2000 */
2001 if (next_match_idx < 0 || next_match_col < (int)current_col)
2002 {
2003 /*
2004 * Check all relevant patterns for a match at this
2005 * position. This is complicated, because matching with a
2006 * pattern takes quite a bit of time, thus we want to
2007 * avoid doing it when it's not needed.
2008 */
2009 next_match_idx = 0; /* no match in this line yet */
2010 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002011 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002012 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002013 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002014 if ( spp->sp_syncing == syncing
2015 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2016 && (spp->sp_type == SPTYPE_MATCH
2017 || spp->sp_type == SPTYPE_START)
2018 && (current_next_list != NULL
2019 ? in_id_list(NULL, current_next_list,
2020 &spp->sp_syn, 0)
2021 : (cur_si == NULL
2022 ? !(spp->sp_flags & HL_CONTAINED)
2023 : in_id_list(cur_si,
2024 cur_si->si_cont_list, &spp->sp_syn,
2025 spp->sp_flags & HL_CONTAINED))))
2026 {
2027 /* If we already tried matching in this line, and
2028 * there isn't a match before next_match_col, skip
2029 * this item. */
2030 if (spp->sp_line_id == current_line_id
2031 && spp->sp_startcol >= next_match_col)
2032 continue;
2033 spp->sp_line_id = current_line_id;
2034
2035 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2036 if (lc_col < 0)
2037 lc_col = 0;
2038
2039 regmatch.rmm_ic = spp->sp_ic;
2040 regmatch.regprog = spp->sp_prog;
2041 if (!syn_regexec(&regmatch, current_lnum,
2042 (colnr_T)lc_col))
2043 {
2044 /* no match in this line, try another one */
2045 spp->sp_startcol = MAXCOL;
2046 continue;
2047 }
2048
2049 /*
2050 * Compute the first column of the match.
2051 */
2052 syn_add_start_off(&pos, &regmatch,
2053 spp, SPO_MS_OFF, -1);
2054 if (pos.lnum > current_lnum)
2055 {
2056 /* must have used end of match in a next line,
2057 * we can't handle that */
2058 spp->sp_startcol = MAXCOL;
2059 continue;
2060 }
2061 startcol = pos.col;
2062
2063 /* remember the next column where this pattern
2064 * matches in the current line */
2065 spp->sp_startcol = startcol;
2066
2067 /*
2068 * If a previously found match starts at a lower
2069 * column number, don't use this one.
2070 */
2071 if (startcol >= next_match_col)
2072 continue;
2073
2074 /*
2075 * If we matched this pattern at this position
2076 * before, skip it. Must retry in the next
2077 * column, because it may match from there.
2078 */
2079 if (did_match_already(idx, &zero_width_next_ga))
2080 {
2081 try_next_column = TRUE;
2082 continue;
2083 }
2084
2085 endpos.lnum = regmatch.endpos[0].lnum;
2086 endpos.col = regmatch.endpos[0].col;
2087
2088 /* Compute the highlight start. */
2089 syn_add_start_off(&hl_startpos, &regmatch,
2090 spp, SPO_HS_OFF, -1);
2091
2092 /* Compute the region start. */
2093 /* Default is to use the end of the match. */
2094 syn_add_end_off(&eos_pos, &regmatch,
2095 spp, SPO_RS_OFF, 0);
2096
2097 /*
2098 * Grab the external submatches before they get
2099 * overwritten. Reference count doesn't change.
2100 */
2101 unref_extmatch(cur_extmatch);
2102 cur_extmatch = re_extmatch_out;
2103 re_extmatch_out = NULL;
2104
2105 flags = 0;
2106 eoe_pos.lnum = 0; /* avoid warning */
2107 eoe_pos.col = 0;
2108 end_idx = 0;
2109 hl_endpos.lnum = 0;
2110
2111 /*
2112 * For a "oneline" the end must be found in the
2113 * same line too. Search for it after the end of
2114 * the match with the start pattern. Set the
2115 * resulting end positions at the same time.
2116 */
2117 if (spp->sp_type == SPTYPE_START
2118 && (spp->sp_flags & HL_ONELINE))
2119 {
2120 lpos_T startpos;
2121
2122 startpos = endpos;
2123 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2124 &flags, &eoe_pos, &end_idx, cur_extmatch);
2125 if (endpos.lnum == 0)
2126 continue; /* not found */
2127 }
2128
2129 /*
2130 * For a "match" the size must be > 0 after the
2131 * end offset needs has been added. Except when
2132 * syncing.
2133 */
2134 else if (spp->sp_type == SPTYPE_MATCH)
2135 {
2136 syn_add_end_off(&hl_endpos, &regmatch, spp,
2137 SPO_HE_OFF, 0);
2138 syn_add_end_off(&endpos, &regmatch, spp,
2139 SPO_ME_OFF, 0);
2140 if (endpos.lnum == current_lnum
2141 && (int)endpos.col + syncing < startcol)
2142 {
2143 /*
2144 * If an empty string is matched, may need
2145 * to try matching again at next column.
2146 */
2147 if (regmatch.startpos[0].col
2148 == regmatch.endpos[0].col)
2149 try_next_column = TRUE;
2150 continue;
2151 }
2152 }
2153
2154 /*
2155 * keep the best match so far in next_match_*
2156 */
2157 /* Highlighting must start after startpos and end
2158 * before endpos. */
2159 if (hl_startpos.lnum == current_lnum
2160 && (int)hl_startpos.col < startcol)
2161 hl_startpos.col = startcol;
2162 limit_pos_zero(&hl_endpos, &endpos);
2163
2164 next_match_idx = idx;
2165 next_match_col = startcol;
2166 next_match_m_endpos = endpos;
2167 next_match_h_endpos = hl_endpos;
2168 next_match_h_startpos = hl_startpos;
2169 next_match_flags = flags;
2170 next_match_eos_pos = eos_pos;
2171 next_match_eoe_pos = eoe_pos;
2172 next_match_end_idx = end_idx;
2173 unref_extmatch(next_match_extmatch);
2174 next_match_extmatch = cur_extmatch;
2175 cur_extmatch = NULL;
2176 }
2177 }
2178 }
2179
2180 /*
2181 * If we found a match at the current column, use it.
2182 */
2183 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2184 {
2185 synpat_T *lspp;
2186
2187 /* When a zero-width item matched which has a nextgroup,
2188 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002189 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002190 if (next_match_m_endpos.lnum == current_lnum
2191 && next_match_m_endpos.col == current_col
2192 && lspp->sp_next_list != NULL)
2193 {
2194 current_next_list = lspp->sp_next_list;
2195 current_next_flags = lspp->sp_flags;
2196 keep_next_list = TRUE;
2197 zero_width_next_list = TRUE;
2198
2199 /* Add the index to a list, so that we can check
2200 * later that we don't match it again (and cause an
2201 * endless loop). */
2202 if (ga_grow(&zero_width_next_ga, 1) == OK)
2203 {
2204 ((int *)(zero_width_next_ga.ga_data))
2205 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002206 }
2207 next_match_idx = -1;
2208 }
2209 else
2210 cur_si = push_next_match(cur_si);
2211 found_match = TRUE;
2212 }
2213 }
2214 }
2215
2216 /*
2217 * Handle searching for nextgroup match.
2218 */
2219 if (current_next_list != NULL && !keep_next_list)
2220 {
2221 /*
2222 * If a nextgroup was not found, continue looking for one if:
2223 * - this is an empty line and the "skipempty" option was given
2224 * - we are on white space and the "skipwhite" option was given
2225 */
2226 if (!found_match)
2227 {
2228 line = syn_getcurline();
2229 if (((current_next_flags & HL_SKIPWHITE)
2230 && vim_iswhite(line[current_col]))
2231 || ((current_next_flags & HL_SKIPEMPTY)
2232 && *line == NUL))
2233 break;
2234 }
2235
2236 /*
2237 * If a nextgroup was found: Use it, and continue looking for
2238 * contained matches.
2239 * If a nextgroup was not found: Continue looking for a normal
2240 * match.
2241 * When did set current_next_list for a zero-width item and no
2242 * match was found don't loop (would get stuck).
2243 */
2244 current_next_list = NULL;
2245 next_match_idx = -1;
2246 if (!zero_width_next_list)
2247 found_match = TRUE;
2248 }
2249
2250 } while (found_match);
2251
2252 /*
2253 * Use attributes from the current state, if within its highlighting.
2254 * If not, use attributes from the current-but-one state, etc.
2255 */
2256 current_attr = 0;
2257#ifdef FEAT_EVAL
2258 current_id = 0;
2259 current_trans_id = 0;
2260#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002261#ifdef FEAT_CONCEAL
2262 current_flags = 0;
2263#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002264 if (cur_si != NULL)
2265 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002266#ifndef FEAT_EVAL
2267 int current_trans_id = 0;
2268#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002269 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2270 {
2271 sip = &CUR_STATE(idx);
2272 if ((current_lnum > sip->si_h_startpos.lnum
2273 || (current_lnum == sip->si_h_startpos.lnum
2274 && current_col >= sip->si_h_startpos.col))
2275 && (sip->si_h_endpos.lnum == 0
2276 || current_lnum < sip->si_h_endpos.lnum
2277 || (current_lnum == sip->si_h_endpos.lnum
2278 && current_col < sip->si_h_endpos.col)))
2279 {
2280 current_attr = sip->si_attr;
2281#ifdef FEAT_EVAL
2282 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002283#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002284 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002285#ifdef FEAT_CONCEAL
2286 current_flags = sip->si_flags;
2287 current_sub_char = sip->si_char;
2288#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002289 break;
2290 }
2291 }
2292
Bram Moolenaar217ad922005-03-20 22:37:15 +00002293 if (can_spell != NULL)
2294 {
2295 struct sp_syn sps;
2296
2297 /*
2298 * set "can_spell" to TRUE if spell checking is supposed to be
2299 * done in the current item.
2300 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002301 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002302 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002303 /* There is no @Spell cluster: Do spelling for items without
2304 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002305 if (syn_block->b_nospell_cluster_id == 0
2306 || current_trans_id == 0)
2307 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002308 else
2309 {
2310 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002311 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002312 sps.cont_in_list = NULL;
2313 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2314 }
2315 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002316 else
2317 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002318 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002319 * the @Spell cluster. But not when @NoSpell is also there.
2320 * At the toplevel only spell check when ":syn spell toplevel"
2321 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002322 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002323 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002324 else
2325 {
2326 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002327 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002328 sps.cont_in_list = NULL;
2329 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2330
Bram Moolenaar860cae12010-06-05 23:22:07 +02002331 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002332 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002333 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002334 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2335 *can_spell = FALSE;
2336 }
2337 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002338 }
2339 }
2340
2341
Bram Moolenaar071d4272004-06-13 20:20:40 +00002342 /*
2343 * Check for end of current state (and the states before it) at the
2344 * next column. Don't do this for syncing, because we would miss a
2345 * single character match.
2346 * First check if the current state ends at the current column. It
2347 * may be for an empty match and a containing item might end in the
2348 * current column.
2349 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002350 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002351 {
2352 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002353 if (current_state.ga_len > 0
2354 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002355 {
2356 ++current_col;
2357 check_state_ends();
2358 --current_col;
2359 }
2360 }
2361 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002362 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002363 /* Default: Only do spelling when there is no @Spell cluster or when
2364 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002365 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2366 ? (syn_block->b_spell_cluster_id == 0)
2367 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002368
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002369 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002370 if (current_next_list != NULL
2371 && syn_getcurline()[current_col + 1] == NUL
2372 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2373 current_next_list = NULL;
2374
2375 if (zero_width_next_ga.ga_len > 0)
2376 ga_clear(&zero_width_next_ga);
2377
2378 /* No longer need external matches. But keep next_match_extmatch. */
2379 unref_extmatch(re_extmatch_out);
2380 re_extmatch_out = NULL;
2381 unref_extmatch(cur_extmatch);
2382
2383 return current_attr;
2384}
2385
2386
2387/*
2388 * Check if we already matched pattern "idx" at the current column.
2389 */
2390 static int
2391did_match_already(idx, gap)
2392 int idx;
2393 garray_T *gap;
2394{
2395 int i;
2396
2397 for (i = current_state.ga_len; --i >= 0; )
2398 if (CUR_STATE(i).si_m_startcol == (int)current_col
2399 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2400 && CUR_STATE(i).si_idx == idx)
2401 return TRUE;
2402
2403 /* Zero-width matches with a nextgroup argument are not put on the syntax
2404 * stack, and can only be matched once anyway. */
2405 for (i = gap->ga_len; --i >= 0; )
2406 if (((int *)(gap->ga_data))[i] == idx)
2407 return TRUE;
2408
2409 return FALSE;
2410}
2411
2412/*
2413 * Push the next match onto the stack.
2414 */
2415 static stateitem_T *
2416push_next_match(cur_si)
2417 stateitem_T *cur_si;
2418{
2419 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002420#ifdef FEAT_CONCEAL
2421 int save_flags;
2422#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002423
Bram Moolenaar860cae12010-06-05 23:22:07 +02002424 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002425
2426 /*
2427 * Push the item in current_state stack;
2428 */
2429 if (push_current_state(next_match_idx) == OK)
2430 {
2431 /*
2432 * If it's a start-skip-end type that crosses lines, figure out how
2433 * much it continues in this line. Otherwise just fill in the length.
2434 */
2435 cur_si = &CUR_STATE(current_state.ga_len - 1);
2436 cur_si->si_h_startpos = next_match_h_startpos;
2437 cur_si->si_m_startcol = current_col;
2438 cur_si->si_m_lnum = current_lnum;
2439 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002440#ifdef FEAT_CONCEAL
2441 cur_si->si_char = spp->sp_char;
2442 if (current_state.ga_len > 1)
2443 cur_si->si_flags |=
2444 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2445#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002446 cur_si->si_next_list = spp->sp_next_list;
2447 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2448 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2449 {
2450 /* Try to find the end pattern in the current line */
2451 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2452 check_keepend();
2453 }
2454 else
2455 {
2456 cur_si->si_m_endpos = next_match_m_endpos;
2457 cur_si->si_h_endpos = next_match_h_endpos;
2458 cur_si->si_ends = TRUE;
2459 cur_si->si_flags |= next_match_flags;
2460 cur_si->si_eoe_pos = next_match_eoe_pos;
2461 cur_si->si_end_idx = next_match_end_idx;
2462 }
2463 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2464 keepend_level = current_state.ga_len - 1;
2465 check_keepend();
2466 update_si_attr(current_state.ga_len - 1);
2467
Bram Moolenaar860cae12010-06-05 23:22:07 +02002468#ifdef FEAT_CONCEAL
2469 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2470#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002471 /*
2472 * If the start pattern has another highlight group, push another item
2473 * on the stack for the start pattern.
2474 */
2475 if ( spp->sp_type == SPTYPE_START
2476 && spp->sp_syn_match_id != 0
2477 && push_current_state(next_match_idx) == OK)
2478 {
2479 cur_si = &CUR_STATE(current_state.ga_len - 1);
2480 cur_si->si_h_startpos = next_match_h_startpos;
2481 cur_si->si_m_startcol = current_col;
2482 cur_si->si_m_lnum = current_lnum;
2483 cur_si->si_m_endpos = next_match_eos_pos;
2484 cur_si->si_h_endpos = next_match_eos_pos;
2485 cur_si->si_ends = TRUE;
2486 cur_si->si_end_idx = 0;
2487 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002488#ifdef FEAT_CONCEAL
2489 cur_si->si_flags |= save_flags;
2490 if (cur_si->si_flags & HL_CONCEALENDS)
2491 cur_si->si_flags |= HL_CONCEAL;
2492#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002493 cur_si->si_next_list = NULL;
2494 check_keepend();
2495 update_si_attr(current_state.ga_len - 1);
2496 }
2497 }
2498
2499 next_match_idx = -1; /* try other match next time */
2500
2501 return cur_si;
2502}
2503
2504/*
2505 * Check for end of current state (and the states before it).
2506 */
2507 static void
2508check_state_ends()
2509{
2510 stateitem_T *cur_si;
2511 int had_extend = FALSE;
2512
2513 cur_si = &CUR_STATE(current_state.ga_len - 1);
2514 for (;;)
2515 {
2516 if (cur_si->si_ends
2517 && (cur_si->si_m_endpos.lnum < current_lnum
2518 || (cur_si->si_m_endpos.lnum == current_lnum
2519 && cur_si->si_m_endpos.col <= current_col)))
2520 {
2521 /*
2522 * If there is an end pattern group ID, highlight the end pattern
2523 * now. No need to pop the current item from the stack.
2524 * Only do this if the end pattern continues beyond the current
2525 * position.
2526 */
2527 if (cur_si->si_end_idx
2528 && (cur_si->si_eoe_pos.lnum > current_lnum
2529 || (cur_si->si_eoe_pos.lnum == current_lnum
2530 && cur_si->si_eoe_pos.col > current_col)))
2531 {
2532 cur_si->si_idx = cur_si->si_end_idx;
2533 cur_si->si_end_idx = 0;
2534 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2535 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2536 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002537#ifdef FEAT_CONCEAL
2538 if (cur_si->si_flags & HL_CONCEALENDS)
2539 cur_si->si_flags |= HL_CONCEAL;
2540#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002541 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002542
2543 /* what matches next may be different now, clear it */
2544 next_match_idx = 0;
2545 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002546 break;
2547 }
2548 else
2549 {
2550 /* handle next_list, unless at end of line and no "skipnl" or
2551 * "skipempty" */
2552 current_next_list = cur_si->si_next_list;
2553 current_next_flags = cur_si->si_flags;
2554 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2555 && syn_getcurline()[current_col] == NUL)
2556 current_next_list = NULL;
2557
2558 /* When the ended item has "extend", another item with
2559 * "keepend" now needs to check for its end. */
2560 if (cur_si->si_flags & HL_EXTEND)
2561 had_extend = TRUE;
2562
2563 pop_current_state();
2564
2565 if (current_state.ga_len == 0)
2566 break;
2567
Bram Moolenaar81993f42008-01-11 20:27:45 +00002568 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002569 {
2570 syn_update_ends(FALSE);
2571 if (current_state.ga_len == 0)
2572 break;
2573 }
2574
2575 cur_si = &CUR_STATE(current_state.ga_len - 1);
2576
2577 /*
2578 * Only for a region the search for the end continues after
2579 * the end of the contained item. If the contained match
2580 * included the end-of-line, break here, the region continues.
2581 * Don't do this when:
2582 * - "keepend" is used for the contained item
2583 * - not at the end of the line (could be end="x$"me=e-1).
2584 * - "excludenl" is used (HL_HAS_EOL won't be set)
2585 */
2586 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002587 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002588 == SPTYPE_START
2589 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2590 {
2591 update_si_end(cur_si, (int)current_col, TRUE);
2592 check_keepend();
2593 if ((current_next_flags & HL_HAS_EOL)
2594 && keepend_level < 0
2595 && syn_getcurline()[current_col] == NUL)
2596 break;
2597 }
2598 }
2599 }
2600 else
2601 break;
2602 }
2603}
2604
2605/*
2606 * Update an entry in the current_state stack for a match or region. This
2607 * fills in si_attr, si_next_list and si_cont_list.
2608 */
2609 static void
2610update_si_attr(idx)
2611 int idx;
2612{
2613 stateitem_T *sip = &CUR_STATE(idx);
2614 synpat_T *spp;
2615
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002616 /* This should not happen... */
2617 if (sip->si_idx < 0)
2618 return;
2619
Bram Moolenaar860cae12010-06-05 23:22:07 +02002620 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002621 if (sip->si_flags & HL_MATCH)
2622 sip->si_id = spp->sp_syn_match_id;
2623 else
2624 sip->si_id = spp->sp_syn.id;
2625 sip->si_attr = syn_id2attr(sip->si_id);
2626 sip->si_trans_id = sip->si_id;
2627 if (sip->si_flags & HL_MATCH)
2628 sip->si_cont_list = NULL;
2629 else
2630 sip->si_cont_list = spp->sp_cont_list;
2631
2632 /*
2633 * For transparent items, take attr from outer item.
2634 * Also take cont_list, if there is none.
2635 * Don't do this for the matchgroup of a start or end pattern.
2636 */
2637 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2638 {
2639 if (idx == 0)
2640 {
2641 sip->si_attr = 0;
2642 sip->si_trans_id = 0;
2643 if (sip->si_cont_list == NULL)
2644 sip->si_cont_list = ID_LIST_ALL;
2645 }
2646 else
2647 {
2648 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2649 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002650 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2651 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002652 if (sip->si_cont_list == NULL)
2653 {
2654 sip->si_flags |= HL_TRANS_CONT;
2655 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2656 }
2657 }
2658 }
2659}
2660
2661/*
2662 * Check the current stack for patterns with "keepend" flag.
2663 * Propagate the match-end to contained items, until a "skipend" item is found.
2664 */
2665 static void
2666check_keepend()
2667{
2668 int i;
2669 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002670 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002671 stateitem_T *sip;
2672
2673 /*
2674 * This check can consume a lot of time; only do it from the level where
2675 * there really is a keepend.
2676 */
2677 if (keepend_level < 0)
2678 return;
2679
2680 /*
2681 * Find the last index of an "extend" item. "keepend" items before that
2682 * won't do anything. If there is no "extend" item "i" will be
2683 * "keepend_level" and all "keepend" items will work normally.
2684 */
2685 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2686 if (CUR_STATE(i).si_flags & HL_EXTEND)
2687 break;
2688
2689 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002690 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002691 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002692 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002693 for ( ; i < current_state.ga_len; ++i)
2694 {
2695 sip = &CUR_STATE(i);
2696 if (maxpos.lnum != 0)
2697 {
2698 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002699 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002700 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2701 sip->si_ends = TRUE;
2702 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002703 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2704 {
2705 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002706 || maxpos.lnum > sip->si_m_endpos.lnum
2707 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002708 && maxpos.col > sip->si_m_endpos.col))
2709 maxpos = sip->si_m_endpos;
2710 if (maxpos_h.lnum == 0
2711 || maxpos_h.lnum > sip->si_h_endpos.lnum
2712 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2713 && maxpos_h.col > sip->si_h_endpos.col))
2714 maxpos_h = sip->si_h_endpos;
2715 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002716 }
2717}
2718
2719/*
2720 * Update an entry in the current_state stack for a start-skip-end pattern.
2721 * This finds the end of the current item, if it's in the current line.
2722 *
2723 * Return the flags for the matched END.
2724 */
2725 static void
2726update_si_end(sip, startcol, force)
2727 stateitem_T *sip;
2728 int startcol; /* where to start searching for the end */
2729 int force; /* when TRUE overrule a previous end */
2730{
2731 lpos_T startpos;
2732 lpos_T endpos;
2733 lpos_T hl_endpos;
2734 lpos_T end_endpos;
2735 int end_idx;
2736
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002737 /* return quickly for a keyword */
2738 if (sip->si_idx < 0)
2739 return;
2740
Bram Moolenaar071d4272004-06-13 20:20:40 +00002741 /* Don't update when it's already done. Can be a match of an end pattern
2742 * that started in a previous line. Watch out: can also be a "keepend"
2743 * from a containing item. */
2744 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2745 return;
2746
2747 /*
2748 * We need to find the end of the region. It may continue in the next
2749 * line.
2750 */
2751 end_idx = 0;
2752 startpos.lnum = current_lnum;
2753 startpos.col = startcol;
2754 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2755 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2756
2757 if (endpos.lnum == 0)
2758 {
2759 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002760 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002761 {
2762 /* a "oneline" never continues in the next line */
2763 sip->si_ends = TRUE;
2764 sip->si_m_endpos.lnum = current_lnum;
2765 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2766 }
2767 else
2768 {
2769 /* continues in the next line */
2770 sip->si_ends = FALSE;
2771 sip->si_m_endpos.lnum = 0;
2772 }
2773 sip->si_h_endpos = sip->si_m_endpos;
2774 }
2775 else
2776 {
2777 /* match within this line */
2778 sip->si_m_endpos = endpos;
2779 sip->si_h_endpos = hl_endpos;
2780 sip->si_eoe_pos = end_endpos;
2781 sip->si_ends = TRUE;
2782 sip->si_end_idx = end_idx;
2783 }
2784}
2785
2786/*
2787 * Add a new state to the current state stack.
2788 * It is cleared and the index set to "idx".
2789 * Return FAIL if it's not possible (out of memory).
2790 */
2791 static int
2792push_current_state(idx)
2793 int idx;
2794{
2795 if (ga_grow(&current_state, 1) == FAIL)
2796 return FAIL;
2797 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2798 CUR_STATE(current_state.ga_len).si_idx = idx;
2799 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002800 return OK;
2801}
2802
2803/*
2804 * Remove a state from the current_state stack.
2805 */
2806 static void
2807pop_current_state()
2808{
2809 if (current_state.ga_len)
2810 {
2811 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2812 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002813 }
2814 /* after the end of a pattern, try matching a keyword or pattern */
2815 next_match_idx = -1;
2816
2817 /* if first state with "keepend" is popped, reset keepend_level */
2818 if (keepend_level >= current_state.ga_len)
2819 keepend_level = -1;
2820}
2821
2822/*
2823 * Find the end of a start/skip/end syntax region after "startpos".
2824 * Only checks one line.
2825 * Also handles a match item that continued from a previous line.
2826 * If not found, the syntax item continues in the next line. m_endpos->lnum
2827 * will be 0.
2828 * If found, the end of the region and the end of the highlighting is
2829 * computed.
2830 */
2831 static void
2832find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2833 end_idx, start_ext)
2834 int idx; /* index of the pattern */
2835 lpos_T *startpos; /* where to start looking for an END match */
2836 lpos_T *m_endpos; /* return: end of match */
2837 lpos_T *hl_endpos; /* return: end of highlighting */
2838 long *flagsp; /* return: flags of matching END */
2839 lpos_T *end_endpos; /* return: end of end pattern match */
2840 int *end_idx; /* return: group ID for end pat. match, or 0 */
2841 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2842{
2843 colnr_T matchcol;
2844 synpat_T *spp, *spp_skip;
2845 int start_idx;
2846 int best_idx;
2847 regmmatch_T regmatch;
2848 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2849 lpos_T pos;
2850 char_u *line;
2851 int had_match = FALSE;
2852
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002853 /* just in case we are invoked for a keyword */
2854 if (idx < 0)
2855 return;
2856
Bram Moolenaar071d4272004-06-13 20:20:40 +00002857 /*
2858 * Check for being called with a START pattern.
2859 * Can happen with a match that continues to the next line, because it
2860 * contained a region.
2861 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002862 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002863 if (spp->sp_type != SPTYPE_START)
2864 {
2865 *hl_endpos = *startpos;
2866 return;
2867 }
2868
2869 /*
2870 * Find the SKIP or first END pattern after the last START pattern.
2871 */
2872 for (;;)
2873 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002874 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002875 if (spp->sp_type != SPTYPE_START)
2876 break;
2877 ++idx;
2878 }
2879
2880 /*
2881 * Lookup the SKIP pattern (if present)
2882 */
2883 if (spp->sp_type == SPTYPE_SKIP)
2884 {
2885 spp_skip = spp;
2886 ++idx;
2887 }
2888 else
2889 spp_skip = NULL;
2890
2891 /* Setup external matches for syn_regexec(). */
2892 unref_extmatch(re_extmatch_in);
2893 re_extmatch_in = ref_extmatch(start_ext);
2894
2895 matchcol = startpos->col; /* start looking for a match at sstart */
2896 start_idx = idx; /* remember the first END pattern. */
2897 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2898 for (;;)
2899 {
2900 /*
2901 * Find end pattern that matches first after "matchcol".
2902 */
2903 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002904 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002905 {
2906 int lc_col = matchcol;
2907
Bram Moolenaar860cae12010-06-05 23:22:07 +02002908 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002909 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2910 break;
2911 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2912 if (lc_col < 0)
2913 lc_col = 0;
2914
2915 regmatch.rmm_ic = spp->sp_ic;
2916 regmatch.regprog = spp->sp_prog;
2917 if (syn_regexec(&regmatch, startpos->lnum, lc_col))
2918 {
2919 if (best_idx == -1 || regmatch.startpos[0].col
2920 < best_regmatch.startpos[0].col)
2921 {
2922 best_idx = idx;
2923 best_regmatch.startpos[0] = regmatch.startpos[0];
2924 best_regmatch.endpos[0] = regmatch.endpos[0];
2925 }
2926 }
2927 }
2928
2929 /*
2930 * If all end patterns have been tried, and there is no match, the
2931 * item continues until end-of-line.
2932 */
2933 if (best_idx == -1)
2934 break;
2935
2936 /*
2937 * If the skip pattern matches before the end pattern,
2938 * continue searching after the skip pattern.
2939 */
2940 if (spp_skip != NULL)
2941 {
2942 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2943
2944 if (lc_col < 0)
2945 lc_col = 0;
2946 regmatch.rmm_ic = spp_skip->sp_ic;
2947 regmatch.regprog = spp_skip->sp_prog;
2948 if (syn_regexec(&regmatch, startpos->lnum, lc_col)
2949 && regmatch.startpos[0].col
2950 <= best_regmatch.startpos[0].col)
2951 {
2952 /* Add offset to skip pattern match */
2953 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2954
2955 /* If the skip pattern goes on to the next line, there is no
2956 * match with an end pattern in this line. */
2957 if (pos.lnum > startpos->lnum)
2958 break;
2959
2960 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
2961
2962 /* take care of an empty match or negative offset */
2963 if (pos.col <= matchcol)
2964 ++matchcol;
2965 else if (pos.col <= regmatch.endpos[0].col)
2966 matchcol = pos.col;
2967 else
2968 /* Be careful not to jump over the NUL at the end-of-line */
2969 for (matchcol = regmatch.endpos[0].col;
2970 line[matchcol] != NUL && matchcol < pos.col;
2971 ++matchcol)
2972 ;
2973
2974 /* if the skip pattern includes end-of-line, break here */
2975 if (line[matchcol] == NUL)
2976 break;
2977
2978 continue; /* start with first end pattern again */
2979 }
2980 }
2981
2982 /*
2983 * Match from start pattern to end pattern.
2984 * Correct for match and highlight offset of end pattern.
2985 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002986 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002987 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
2988 /* can't end before the start */
2989 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
2990 m_endpos->col = startpos->col;
2991
2992 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
2993 /* can't end before the start */
2994 if (end_endpos->lnum == startpos->lnum
2995 && end_endpos->col < startpos->col)
2996 end_endpos->col = startpos->col;
2997 /* can't end after the match */
2998 limit_pos(end_endpos, m_endpos);
2999
3000 /*
3001 * If the end group is highlighted differently, adjust the pointers.
3002 */
3003 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3004 {
3005 *end_idx = best_idx;
3006 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3007 {
3008 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3009 hl_endpos->col = best_regmatch.endpos[0].col;
3010 }
3011 else
3012 {
3013 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3014 hl_endpos->col = best_regmatch.startpos[0].col;
3015 }
3016 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3017
3018 /* can't end before the start */
3019 if (hl_endpos->lnum == startpos->lnum
3020 && hl_endpos->col < startpos->col)
3021 hl_endpos->col = startpos->col;
3022 limit_pos(hl_endpos, m_endpos);
3023
3024 /* now the match ends where the highlighting ends, it is turned
3025 * into the matchgroup for the end */
3026 *m_endpos = *hl_endpos;
3027 }
3028 else
3029 {
3030 *end_idx = 0;
3031 *hl_endpos = *end_endpos;
3032 }
3033
3034 *flagsp = spp->sp_flags;
3035
3036 had_match = TRUE;
3037 break;
3038 }
3039
3040 /* no match for an END pattern in this line */
3041 if (!had_match)
3042 m_endpos->lnum = 0;
3043
3044 /* Remove external matches. */
3045 unref_extmatch(re_extmatch_in);
3046 re_extmatch_in = NULL;
3047}
3048
3049/*
3050 * Limit "pos" not to be after "limit".
3051 */
3052 static void
3053limit_pos(pos, limit)
3054 lpos_T *pos;
3055 lpos_T *limit;
3056{
3057 if (pos->lnum > limit->lnum)
3058 *pos = *limit;
3059 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3060 pos->col = limit->col;
3061}
3062
3063/*
3064 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3065 */
3066 static void
3067limit_pos_zero(pos, limit)
3068 lpos_T *pos;
3069 lpos_T *limit;
3070{
3071 if (pos->lnum == 0)
3072 *pos = *limit;
3073 else
3074 limit_pos(pos, limit);
3075}
3076
3077/*
3078 * Add offset to matched text for end of match or highlight.
3079 */
3080 static void
3081syn_add_end_off(result, regmatch, spp, idx, extra)
3082 lpos_T *result; /* returned position */
3083 regmmatch_T *regmatch; /* start/end of match */
3084 synpat_T *spp; /* matched pattern */
3085 int idx; /* index of offset */
3086 int extra; /* extra chars for offset to start */
3087{
3088 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003089 int off;
3090 char_u *base;
3091 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003092
3093 if (spp->sp_off_flags & (1 << idx))
3094 {
3095 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003096 col = regmatch->startpos[0].col;
3097 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003098 }
3099 else
3100 {
3101 result->lnum = regmatch->endpos[0].lnum;
3102 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003103 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003104 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003105 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3106 * is a matchgroup. Watch out for match with last NL in the buffer. */
3107 if (result->lnum > syn_buf->b_ml.ml_line_count)
3108 col = 0;
3109 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003110 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003111 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3112 p = base + col;
3113 if (off > 0)
3114 {
3115 while (off-- > 0 && *p != NUL)
3116 mb_ptr_adv(p);
3117 }
3118 else if (off < 0)
3119 {
3120 while (off++ < 0 && base < p)
3121 mb_ptr_back(base, p);
3122 }
3123 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003124 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003125 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003126}
3127
3128/*
3129 * Add offset to matched text for start of match or highlight.
3130 * Avoid resulting column to become negative.
3131 */
3132 static void
3133syn_add_start_off(result, regmatch, spp, idx, extra)
3134 lpos_T *result; /* returned position */
3135 regmmatch_T *regmatch; /* start/end of match */
3136 synpat_T *spp;
3137 int idx;
3138 int extra; /* extra chars for offset to end */
3139{
3140 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003141 int off;
3142 char_u *base;
3143 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003144
3145 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3146 {
3147 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003148 col = regmatch->endpos[0].col;
3149 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003150 }
3151 else
3152 {
3153 result->lnum = regmatch->startpos[0].lnum;
3154 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003155 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003156 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003157 if (result->lnum > syn_buf->b_ml.ml_line_count)
3158 {
3159 /* a "\n" at the end of the pattern may take us below the last line */
3160 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003161 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003162 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003163 if (off != 0)
3164 {
3165 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3166 p = base + col;
3167 if (off > 0)
3168 {
3169 while (off-- && *p != NUL)
3170 mb_ptr_adv(p);
3171 }
3172 else if (off < 0)
3173 {
3174 while (off++ && base < p)
3175 mb_ptr_back(base, p);
3176 }
3177 col = (int)(p - base);
3178 }
3179 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003180}
3181
3182/*
3183 * Get current line in syntax buffer.
3184 */
3185 static char_u *
3186syn_getcurline()
3187{
3188 return ml_get_buf(syn_buf, current_lnum, FALSE);
3189}
3190
3191/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003192 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003193 * Returns TRUE when there is a match.
3194 */
3195 static int
3196syn_regexec(rmp, lnum, col)
3197 regmmatch_T *rmp;
3198 linenr_T lnum;
3199 colnr_T col;
3200{
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003201 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar91a4e822008-01-19 14:59:58 +00003202 if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003203 {
3204 rmp->startpos[0].lnum += lnum;
3205 rmp->endpos[0].lnum += lnum;
3206 return TRUE;
3207 }
3208 return FALSE;
3209}
3210
3211/*
3212 * Check one position in a line for a matching keyword.
3213 * The caller must check if a keyword can start at startcol.
3214 * Return it's ID if found, 0 otherwise.
3215 */
3216 static int
Bram Moolenaar860cae12010-06-05 23:22:07 +02003217check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si, ccharp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003218 char_u *line;
3219 int startcol; /* position in line to check for keyword */
3220 int *endcolp; /* return: character after found keyword */
3221 long *flagsp; /* return: flags of matching keyword */
3222 short **next_listp; /* return: next_list of matching keyword */
3223 stateitem_T *cur_si; /* item at the top of the stack */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003224 int *ccharp UNUSED; /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003225{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003226 keyentry_T *kp;
3227 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003228 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003229 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003230 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003231 hashtab_T *ht;
3232 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003233
3234 /* Find first character after the keyword. First character was already
3235 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003236 kwp = line + startcol;
3237 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003238 do
3239 {
3240#ifdef FEAT_MBYTE
3241 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003242 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003243 else
3244#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003245 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003246 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003247 while (vim_iswordc_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003248
Bram Moolenaardad6b692005-01-25 22:14:34 +00003249 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003250 return 0;
3251
3252 /*
3253 * Must make a copy of the keyword, so we can add a NUL and make it
3254 * lowercase.
3255 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003256 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003257
3258 /*
3259 * Try twice:
3260 * 1. matching case
3261 * 2. ignoring case
3262 */
3263 for (round = 1; round <= 2; ++round)
3264 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003265 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003266 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003267 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003268 if (round == 2) /* ignore case */
3269 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003270
3271 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003272 * Find keywords that match. There can be several with different
3273 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003274 * When current_next_list is non-zero accept only that group, otherwise:
3275 * Accept a not-contained keyword at toplevel.
3276 * Accept a keyword at other levels only if it is in the contains list.
3277 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003278 hi = hash_find(ht, keyword);
3279 if (!HASHITEM_EMPTY(hi))
3280 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003281 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003282 if (current_next_list != 0
3283 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3284 : (cur_si == NULL
3285 ? !(kp->flags & HL_CONTAINED)
3286 : in_id_list(cur_si, cur_si->si_cont_list,
3287 &kp->k_syn, kp->flags & HL_CONTAINED)))
3288 {
3289 *endcolp = startcol + kwlen;
3290 *flagsp = kp->flags;
3291 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003292#ifdef FEAT_CONCEAL
3293 *ccharp = kp->k_char;
3294#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003295 return kp->k_syn.id;
3296 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003297 }
3298 }
3299 return 0;
3300}
3301
3302/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003303 * Handle ":syntax conceal" command.
3304 */
3305 static void
3306syn_cmd_conceal(eap, syncing)
3307 exarg_T *eap UNUSED;
3308 int syncing UNUSED;
3309{
3310#ifdef FEAT_CONCEAL
3311 char_u *arg = eap->arg;
3312 char_u *next;
3313
3314 eap->nextcmd = find_nextcmd(arg);
3315 if (eap->skip)
3316 return;
3317
3318 next = skiptowhite(arg);
3319 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3320 curwin->w_s->b_syn_conceal = TRUE;
3321 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3322 curwin->w_s->b_syn_conceal = FALSE;
3323 else
3324 EMSG2(_("E390: Illegal argument: %s"), arg);
3325#endif
3326}
3327
3328/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003329 * Handle ":syntax case" command.
3330 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003331 static void
3332syn_cmd_case(eap, syncing)
3333 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003334 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003335{
3336 char_u *arg = eap->arg;
3337 char_u *next;
3338
3339 eap->nextcmd = find_nextcmd(arg);
3340 if (eap->skip)
3341 return;
3342
3343 next = skiptowhite(arg);
3344 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003345 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003346 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003347 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003348 else
3349 EMSG2(_("E390: Illegal argument: %s"), arg);
3350}
3351
3352/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003353 * Handle ":syntax spell" command.
3354 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003355 static void
3356syn_cmd_spell(eap, syncing)
3357 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003358 int syncing UNUSED;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003359{
3360 char_u *arg = eap->arg;
3361 char_u *next;
3362
3363 eap->nextcmd = find_nextcmd(arg);
3364 if (eap->skip)
3365 return;
3366
3367 next = skiptowhite(arg);
3368 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003369 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003370 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003371 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003372 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003373 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003374 else
3375 EMSG2(_("E390: Illegal argument: %s"), arg);
3376}
3377
3378/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003379 * Clear all syntax info for one buffer.
3380 */
3381 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003382syntax_clear(block)
3383 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003384{
3385 int i;
3386
Bram Moolenaar860cae12010-06-05 23:22:07 +02003387 block->b_syn_error = FALSE; /* clear previous error */
3388 block->b_syn_ic = FALSE; /* Use case, by default */
3389 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3390 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003391
3392 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003393 clear_keywtab(&block->b_keywtab);
3394 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003395
3396 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003397 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3398 syn_clear_pattern(block, i);
3399 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003400
3401 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003402 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3403 syn_clear_cluster(block, i);
3404 ga_clear(&block->b_syn_clusters);
3405 block->b_spell_cluster_id = 0;
3406 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003407
Bram Moolenaar860cae12010-06-05 23:22:07 +02003408 block->b_syn_sync_flags = 0;
3409 block->b_syn_sync_minlines = 0;
3410 block->b_syn_sync_maxlines = 0;
3411 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003412
Bram Moolenaar860cae12010-06-05 23:22:07 +02003413 vim_free(block->b_syn_linecont_prog);
3414 block->b_syn_linecont_prog = NULL;
3415 vim_free(block->b_syn_linecont_pat);
3416 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003417#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003418 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003419#endif
3420
3421 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003422 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003423 invalidate_current_state();
3424}
3425
3426/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003427 * Get rid of ownsyntax for window "wp".
3428 */
3429 void
3430reset_synblock(wp)
3431 win_T *wp;
3432{
3433 if (wp->w_s != &wp->w_buffer->b_s)
3434 {
3435 syntax_clear(wp->w_s);
3436 vim_free(wp->w_s);
3437 wp->w_s = &wp->w_buffer->b_s;
3438 }
3439}
3440
3441/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003442 * Clear syncing info for one buffer.
3443 */
3444 static void
3445syntax_sync_clear()
3446{
3447 int i;
3448
3449 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003450 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3451 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3452 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003453
Bram Moolenaar860cae12010-06-05 23:22:07 +02003454 curwin->w_s->b_syn_sync_flags = 0;
3455 curwin->w_s->b_syn_sync_minlines = 0;
3456 curwin->w_s->b_syn_sync_maxlines = 0;
3457 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003458
Bram Moolenaar860cae12010-06-05 23:22:07 +02003459 vim_free(curwin->w_s->b_syn_linecont_prog);
3460 curwin->w_s->b_syn_linecont_prog = NULL;
3461 vim_free(curwin->w_s->b_syn_linecont_pat);
3462 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003463
Bram Moolenaar860cae12010-06-05 23:22:07 +02003464 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003465}
3466
3467/*
3468 * Remove one pattern from the buffer's pattern list.
3469 */
3470 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003471syn_remove_pattern(block, idx)
3472 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003473 int idx;
3474{
3475 synpat_T *spp;
3476
Bram Moolenaar860cae12010-06-05 23:22:07 +02003477 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003478#ifdef FEAT_FOLDING
3479 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003480 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003481#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003482 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003483 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003484 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3485 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003486}
3487
3488/*
3489 * Clear and free one syntax pattern. When clearing all, must be called from
3490 * last to first!
3491 */
3492 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003493syn_clear_pattern(block, i)
3494 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003495 int i;
3496{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003497 vim_free(SYN_ITEMS(block)[i].sp_pattern);
3498 vim_free(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003499 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003500 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003501 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003502 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3503 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3504 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003505 }
3506}
3507
3508/*
3509 * Clear and free one syntax cluster.
3510 */
3511 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003512syn_clear_cluster(block, i)
3513 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003514 int i;
3515{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003516 vim_free(SYN_CLSTR(block)[i].scl_name);
3517 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3518 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003519}
3520
3521/*
3522 * Handle ":syntax clear" command.
3523 */
3524 static void
3525syn_cmd_clear(eap, syncing)
3526 exarg_T *eap;
3527 int syncing;
3528{
3529 char_u *arg = eap->arg;
3530 char_u *arg_end;
3531 int id;
3532
3533 eap->nextcmd = find_nextcmd(arg);
3534 if (eap->skip)
3535 return;
3536
3537 /*
3538 * We have to disable this within ":syn include @group filename",
3539 * because otherwise @group would get deleted.
3540 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3541 * clear".
3542 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003543 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003544 return;
3545
3546 if (ends_excmd(*arg))
3547 {
3548 /*
3549 * No argument: Clear all syntax items.
3550 */
3551 if (syncing)
3552 syntax_sync_clear();
3553 else
3554 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003555 syntax_clear(curwin->w_s);
3556 if (curwin->w_s == &curwin->w_buffer->b_s)
3557 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003558 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003559 }
3560 }
3561 else
3562 {
3563 /*
3564 * Clear the group IDs that are in the argument.
3565 */
3566 while (!ends_excmd(*arg))
3567 {
3568 arg_end = skiptowhite(arg);
3569 if (*arg == '@')
3570 {
3571 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3572 if (id == 0)
3573 {
3574 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3575 break;
3576 }
3577 else
3578 {
3579 /*
3580 * We can't physically delete a cluster without changing
3581 * the IDs of other clusters, so we do the next best thing
3582 * and make it empty.
3583 */
3584 short scl_id = id - SYNID_CLUSTER;
3585
Bram Moolenaar860cae12010-06-05 23:22:07 +02003586 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3587 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003588 }
3589 }
3590 else
3591 {
3592 id = syn_namen2id(arg, (int)(arg_end - arg));
3593 if (id == 0)
3594 {
3595 EMSG2(_(e_nogroup), arg);
3596 break;
3597 }
3598 else
3599 syn_clear_one(id, syncing);
3600 }
3601 arg = skipwhite(arg_end);
3602 }
3603 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003604 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003605 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003606}
3607
3608/*
3609 * Clear one syntax group for the current buffer.
3610 */
3611 static void
3612syn_clear_one(id, syncing)
3613 int id;
3614 int syncing;
3615{
3616 synpat_T *spp;
3617 int idx;
3618
3619 /* Clear keywords only when not ":syn sync clear group-name" */
3620 if (!syncing)
3621 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003622 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3623 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003624 }
3625
3626 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003627 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003628 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003629 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003630 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3631 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003632 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003633 }
3634}
3635
3636/*
3637 * Handle ":syntax on" command.
3638 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003639 static void
3640syn_cmd_on(eap, syncing)
3641 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003642 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003643{
3644 syn_cmd_onoff(eap, "syntax");
3645}
3646
3647/*
3648 * Handle ":syntax enable" command.
3649 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003650 static void
3651syn_cmd_enable(eap, syncing)
3652 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003653 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003654{
3655 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3656 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003657 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003658}
3659
3660/*
3661 * Handle ":syntax reset" command.
3662 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003663 static void
3664syn_cmd_reset(eap, syncing)
3665 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003666 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003667{
3668 eap->nextcmd = check_nextcmd(eap->arg);
3669 if (!eap->skip)
3670 {
3671 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3672 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003673 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003674 }
3675}
3676
3677/*
3678 * Handle ":syntax manual" command.
3679 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003680 static void
3681syn_cmd_manual(eap, syncing)
3682 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003683 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003684{
3685 syn_cmd_onoff(eap, "manual");
3686}
3687
3688/*
3689 * Handle ":syntax off" command.
3690 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003691 static void
3692syn_cmd_off(eap, syncing)
3693 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003694 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003695{
3696 syn_cmd_onoff(eap, "nosyntax");
3697}
3698
3699 static void
3700syn_cmd_onoff(eap, name)
3701 exarg_T *eap;
3702 char *name;
3703{
3704 char_u buf[100];
3705
3706 eap->nextcmd = check_nextcmd(eap->arg);
3707 if (!eap->skip)
3708 {
3709 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003710 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003711 do_cmdline_cmd(buf);
3712 }
3713}
3714
3715/*
3716 * Handle ":syntax [list]" command: list current syntax words.
3717 */
3718 static void
3719syn_cmd_list(eap, syncing)
3720 exarg_T *eap;
3721 int syncing; /* when TRUE: list syncing items */
3722{
3723 char_u *arg = eap->arg;
3724 int id;
3725 char_u *arg_end;
3726
3727 eap->nextcmd = find_nextcmd(arg);
3728 if (eap->skip)
3729 return;
3730
Bram Moolenaar860cae12010-06-05 23:22:07 +02003731 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003732 {
3733 MSG(_("No Syntax items defined for this buffer"));
3734 return;
3735 }
3736
3737 if (syncing)
3738 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003739 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003740 {
3741 MSG_PUTS(_("syncing on C-style comments"));
3742 syn_lines_msg();
3743 syn_match_msg();
3744 return;
3745 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003746 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003747 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003748 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003749 MSG_PUTS(_("no syncing"));
3750 else
3751 {
3752 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003753 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003754 MSG_PUTS(_(" lines before top line"));
3755 syn_match_msg();
3756 }
3757 return;
3758 }
3759 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003760 if (curwin->w_s->b_syn_sync_minlines > 0
3761 || curwin->w_s->b_syn_sync_maxlines > 0
3762 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003763 {
3764 MSG_PUTS(_("\nsyncing on items"));
3765 syn_lines_msg();
3766 syn_match_msg();
3767 }
3768 }
3769 else
3770 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3771 if (ends_excmd(*arg))
3772 {
3773 /*
3774 * No argument: List all group IDs and all syntax clusters.
3775 */
3776 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3777 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003778 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003779 syn_list_cluster(id);
3780 }
3781 else
3782 {
3783 /*
3784 * List the group IDs and syntax clusters that are in the argument.
3785 */
3786 while (!ends_excmd(*arg) && !got_int)
3787 {
3788 arg_end = skiptowhite(arg);
3789 if (*arg == '@')
3790 {
3791 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3792 if (id == 0)
3793 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3794 else
3795 syn_list_cluster(id - SYNID_CLUSTER);
3796 }
3797 else
3798 {
3799 id = syn_namen2id(arg, (int)(arg_end - arg));
3800 if (id == 0)
3801 EMSG2(_(e_nogroup), arg);
3802 else
3803 syn_list_one(id, syncing, TRUE);
3804 }
3805 arg = skipwhite(arg_end);
3806 }
3807 }
3808 eap->nextcmd = check_nextcmd(arg);
3809}
3810
3811 static void
3812syn_lines_msg()
3813{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003814 if (curwin->w_s->b_syn_sync_maxlines > 0
3815 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003816 {
3817 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003818 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003819 {
3820 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003821 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3822 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003823 MSG_PUTS(", ");
3824 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003825 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003826 {
3827 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003828 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003829 }
3830 MSG_PUTS(_(" lines before top line"));
3831 }
3832}
3833
3834 static void
3835syn_match_msg()
3836{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003837 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003838 {
3839 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003840 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003841 MSG_PUTS(_(" line breaks"));
3842 }
3843}
3844
3845static int last_matchgroup;
3846
3847struct name_list
3848{
3849 int flag;
3850 char *name;
3851};
3852
3853static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3854
3855/*
3856 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3857 */
3858 static void
3859syn_list_one(id, syncing, link_only)
3860 int id;
3861 int syncing; /* when TRUE: list syncing items */
3862 int link_only; /* when TRUE; list link-only too */
3863{
3864 int attr;
3865 int idx;
3866 int did_header = FALSE;
3867 synpat_T *spp;
3868 static struct name_list namelist1[] =
3869 {
3870 {HL_DISPLAY, "display"},
3871 {HL_CONTAINED, "contained"},
3872 {HL_ONELINE, "oneline"},
3873 {HL_KEEPEND, "keepend"},
3874 {HL_EXTEND, "extend"},
3875 {HL_EXCLUDENL, "excludenl"},
3876 {HL_TRANSP, "transparent"},
3877 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003878#ifdef FEAT_CONCEAL
3879 {HL_CONCEAL, "conceal"},
3880 {HL_CONCEALENDS, "concealends"},
3881#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003882 {0, NULL}
3883 };
3884 static struct name_list namelist2[] =
3885 {
3886 {HL_SKIPWHITE, "skipwhite"},
3887 {HL_SKIPNL, "skipnl"},
3888 {HL_SKIPEMPTY, "skipempty"},
3889 {0, NULL}
3890 };
3891
3892 attr = hl_attr(HLF_D); /* highlight like directories */
3893
3894 /* list the keywords for "id" */
3895 if (!syncing)
3896 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003897 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3898 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003899 did_header, attr);
3900 }
3901
3902 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003903 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003904 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003905 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003906 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3907 continue;
3908
3909 (void)syn_list_header(did_header, 999, id);
3910 did_header = TRUE;
3911 last_matchgroup = 0;
3912 if (spp->sp_type == SPTYPE_MATCH)
3913 {
3914 put_pattern("match", ' ', spp, attr);
3915 msg_putchar(' ');
3916 }
3917 else if (spp->sp_type == SPTYPE_START)
3918 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003919 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
3920 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3921 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
3922 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3923 while (idx < curwin->w_s->b_syn_patterns.ga_len
3924 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
3925 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003926 --idx;
3927 msg_putchar(' ');
3928 }
3929 syn_list_flags(namelist1, spp->sp_flags, attr);
3930
3931 if (spp->sp_cont_list != NULL)
3932 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3933
3934 if (spp->sp_syn.cont_in_list != NULL)
3935 put_id_list((char_u *)"containedin",
3936 spp->sp_syn.cont_in_list, attr);
3937
3938 if (spp->sp_next_list != NULL)
3939 {
3940 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3941 syn_list_flags(namelist2, spp->sp_flags, attr);
3942 }
3943 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3944 {
3945 if (spp->sp_flags & HL_SYNC_HERE)
3946 msg_puts_attr((char_u *)"grouphere", attr);
3947 else
3948 msg_puts_attr((char_u *)"groupthere", attr);
3949 msg_putchar(' ');
3950 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003951 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003952 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3953 else
3954 MSG_PUTS("NONE");
3955 msg_putchar(' ');
3956 }
3957 }
3958
3959 /* list the link, if there is one */
3960 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3961 {
3962 (void)syn_list_header(did_header, 999, id);
3963 msg_puts_attr((char_u *)"links to", attr);
3964 msg_putchar(' ');
3965 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3966 }
3967}
3968
3969 static void
3970syn_list_flags(nl, flags, attr)
3971 struct name_list *nl;
3972 int flags;
3973 int attr;
3974{
3975 int i;
3976
3977 for (i = 0; nl[i].flag != 0; ++i)
3978 if (flags & nl[i].flag)
3979 {
3980 msg_puts_attr((char_u *)nl[i].name, attr);
3981 msg_putchar(' ');
3982 }
3983}
3984
3985/*
3986 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
3987 */
3988 static void
3989syn_list_cluster(id)
3990 int id;
3991{
3992 int endcol = 15;
3993
3994 /* slight hack: roughly duplicate the guts of syn_list_header() */
3995 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02003996 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003997
3998 if (msg_col >= endcol) /* output at least one space */
3999 endcol = msg_col + 1;
4000 if (Columns <= endcol) /* avoid hang for tiny window */
4001 endcol = Columns - 1;
4002
4003 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004004 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004005 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004006 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004007 hl_attr(HLF_D));
4008 }
4009 else
4010 {
4011 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4012 msg_puts((char_u *)"=NONE");
4013 }
4014}
4015
4016 static void
4017put_id_list(name, list, attr)
4018 char_u *name;
4019 short *list;
4020 int attr;
4021{
4022 short *p;
4023
4024 msg_puts_attr(name, attr);
4025 msg_putchar('=');
4026 for (p = list; *p; ++p)
4027 {
4028 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4029 {
4030 if (p[1])
4031 MSG_PUTS("ALLBUT");
4032 else
4033 MSG_PUTS("ALL");
4034 }
4035 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4036 {
4037 MSG_PUTS("TOP");
4038 }
4039 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4040 {
4041 MSG_PUTS("CONTAINED");
4042 }
4043 else if (*p >= SYNID_CLUSTER)
4044 {
4045 short scl_id = *p - SYNID_CLUSTER;
4046
4047 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004048 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004049 }
4050 else
4051 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4052 if (p[1])
4053 msg_putchar(',');
4054 }
4055 msg_putchar(' ');
4056}
4057
4058 static void
4059put_pattern(s, c, spp, attr)
4060 char *s;
4061 int c;
4062 synpat_T *spp;
4063 int attr;
4064{
4065 long n;
4066 int mask;
4067 int first;
4068 static char *sepchars = "/+=-#@\"|'^&";
4069 int i;
4070
4071 /* May have to write "matchgroup=group" */
4072 if (last_matchgroup != spp->sp_syn_match_id)
4073 {
4074 last_matchgroup = spp->sp_syn_match_id;
4075 msg_puts_attr((char_u *)"matchgroup", attr);
4076 msg_putchar('=');
4077 if (last_matchgroup == 0)
4078 msg_outtrans((char_u *)"NONE");
4079 else
4080 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4081 msg_putchar(' ');
4082 }
4083
4084 /* output the name of the pattern and an '=' or ' ' */
4085 msg_puts_attr((char_u *)s, attr);
4086 msg_putchar(c);
4087
4088 /* output the pattern, in between a char that is not in the pattern */
4089 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4090 if (sepchars[++i] == NUL)
4091 {
4092 i = 0; /* no good char found, just use the first one */
4093 break;
4094 }
4095 msg_putchar(sepchars[i]);
4096 msg_outtrans(spp->sp_pattern);
4097 msg_putchar(sepchars[i]);
4098
4099 /* output any pattern options */
4100 first = TRUE;
4101 for (i = 0; i < SPO_COUNT; ++i)
4102 {
4103 mask = (1 << i);
4104 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4105 {
4106 if (!first)
4107 msg_putchar(','); /* separate with commas */
4108 msg_puts((char_u *)spo_name_tab[i]);
4109 n = spp->sp_offsets[i];
4110 if (i != SPO_LC_OFF)
4111 {
4112 if (spp->sp_off_flags & mask)
4113 msg_putchar('s');
4114 else
4115 msg_putchar('e');
4116 if (n > 0)
4117 msg_putchar('+');
4118 }
4119 if (n || i == SPO_LC_OFF)
4120 msg_outnum(n);
4121 first = FALSE;
4122 }
4123 }
4124 msg_putchar(' ');
4125}
4126
4127/*
4128 * List or clear the keywords for one syntax group.
4129 * Return TRUE if the header has been printed.
4130 */
4131 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00004132syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004133 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004134 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004135 int did_header; /* header has already been printed */
4136 int attr;
4137{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004138 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004139 hashitem_T *hi;
4140 keyentry_T *kp;
4141 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004142 int prev_contained = 0;
4143 short *prev_next_list = NULL;
4144 short *prev_cont_in_list = NULL;
4145 int prev_skipnl = 0;
4146 int prev_skipwhite = 0;
4147 int prev_skipempty = 0;
4148
Bram Moolenaar071d4272004-06-13 20:20:40 +00004149 /*
4150 * Unfortunately, this list of keywords is not sorted on alphabet but on
4151 * hash value...
4152 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004153 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004154 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004155 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004156 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004157 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004158 --todo;
4159 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004160 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004161 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004162 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004163 if (prev_contained != (kp->flags & HL_CONTAINED)
4164 || prev_skipnl != (kp->flags & HL_SKIPNL)
4165 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4166 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4167 || prev_cont_in_list != kp->k_syn.cont_in_list
4168 || prev_next_list != kp->next_list)
4169 outlen = 9999;
4170 else
4171 outlen = (int)STRLEN(kp->keyword);
4172 /* output "contained" and "nextgroup" on each line */
4173 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004174 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004175 prev_contained = 0;
4176 prev_next_list = NULL;
4177 prev_cont_in_list = NULL;
4178 prev_skipnl = 0;
4179 prev_skipwhite = 0;
4180 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004181 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004182 did_header = TRUE;
4183 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004184 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004185 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004186 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004187 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004188 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004189 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004190 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004191 put_id_list((char_u *)"containedin",
4192 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004193 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004194 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004195 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004196 if (kp->next_list != prev_next_list)
4197 {
4198 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4199 msg_putchar(' ');
4200 prev_next_list = kp->next_list;
4201 if (kp->flags & HL_SKIPNL)
4202 {
4203 msg_puts_attr((char_u *)"skipnl", attr);
4204 msg_putchar(' ');
4205 prev_skipnl = (kp->flags & HL_SKIPNL);
4206 }
4207 if (kp->flags & HL_SKIPWHITE)
4208 {
4209 msg_puts_attr((char_u *)"skipwhite", attr);
4210 msg_putchar(' ');
4211 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4212 }
4213 if (kp->flags & HL_SKIPEMPTY)
4214 {
4215 msg_puts_attr((char_u *)"skipempty", attr);
4216 msg_putchar(' ');
4217 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4218 }
4219 }
4220 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004221 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004222 }
4223 }
4224 }
4225
4226 return did_header;
4227}
4228
4229 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004230syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004231 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004232 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004233{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004234 hashitem_T *hi;
4235 keyentry_T *kp;
4236 keyentry_T *kp_prev;
4237 keyentry_T *kp_next;
4238 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004239
Bram Moolenaardad6b692005-01-25 22:14:34 +00004240 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004241 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004242 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004243 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004244 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004245 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004246 --todo;
4247 kp_prev = NULL;
4248 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004249 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004250 if (kp->k_syn.id == id)
4251 {
4252 kp_next = kp->ke_next;
4253 if (kp_prev == NULL)
4254 {
4255 if (kp_next == NULL)
4256 hash_remove(ht, hi);
4257 else
4258 hi->hi_key = KE2HIKEY(kp_next);
4259 }
4260 else
4261 kp_prev->ke_next = kp_next;
4262 vim_free(kp->next_list);
4263 vim_free(kp->k_syn.cont_in_list);
4264 vim_free(kp);
4265 kp = kp_next;
4266 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004267 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004268 {
4269 kp_prev = kp;
4270 kp = kp->ke_next;
4271 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004272 }
4273 }
4274 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004275 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004276}
4277
4278/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004279 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004280 */
4281 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004282clear_keywtab(ht)
4283 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004284{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004285 hashitem_T *hi;
4286 int todo;
4287 keyentry_T *kp;
4288 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004289
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004290 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004291 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004292 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004293 if (!HASHITEM_EMPTY(hi))
4294 {
4295 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004296 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004297 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004298 kp_next = kp->ke_next;
4299 vim_free(kp->next_list);
4300 vim_free(kp->k_syn.cont_in_list);
4301 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004302 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004303 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004304 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004305 hash_clear(ht);
4306 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004307}
4308
4309/*
4310 * Add a keyword to the list of keywords.
4311 */
4312 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02004313add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004314 char_u *name; /* name of keyword */
4315 int id; /* group ID for this keyword */
4316 int flags; /* flags for this keyword */
4317 short *cont_in_list; /* containedin for this keyword */
4318 short *next_list; /* nextgroup for this keyword */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004319 int conceal_char;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004320{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004321 keyentry_T *kp;
4322 hashtab_T *ht;
4323 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004324 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004325 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004326 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004327
Bram Moolenaar860cae12010-06-05 23:22:07 +02004328 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004329 name_ic = str_foldcase(name, (int)STRLEN(name),
4330 name_folded, MAXKEYWLEN + 1);
4331 else
4332 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004333 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4334 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004335 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004336 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004337 kp->k_syn.id = id;
4338 kp->k_syn.inc_tag = current_syn_inc_tag;
4339 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004340 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004341 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004342 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004343 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004344 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004345
Bram Moolenaar860cae12010-06-05 23:22:07 +02004346 if (curwin->w_s->b_syn_ic)
4347 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004348 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004349 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004350
Bram Moolenaardad6b692005-01-25 22:14:34 +00004351 hash = hash_hash(kp->keyword);
4352 hi = hash_lookup(ht, kp->keyword, hash);
4353 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004354 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004355 /* new keyword, add to hashtable */
4356 kp->ke_next = NULL;
4357 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004358 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004359 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004360 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004361 /* keyword already exists, prepend to list */
4362 kp->ke_next = HI2KE(hi);
4363 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004364 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004365}
4366
4367/*
4368 * Get the start and end of the group name argument.
4369 * Return a pointer to the first argument.
4370 * Return NULL if the end of the command was found instead of further args.
4371 */
4372 static char_u *
4373get_group_name(arg, name_end)
4374 char_u *arg; /* start of the argument */
4375 char_u **name_end; /* pointer to end of the name */
4376{
4377 char_u *rest;
4378
4379 *name_end = skiptowhite(arg);
4380 rest = skipwhite(*name_end);
4381
4382 /*
4383 * Check if there are enough arguments. The first argument may be a
4384 * pattern, where '|' is allowed, so only check for NUL.
4385 */
4386 if (ends_excmd(*arg) || *rest == NUL)
4387 return NULL;
4388 return rest;
4389}
4390
4391/*
4392 * Check for syntax command option arguments.
4393 * This can be called at any place in the list of arguments, and just picks
4394 * out the arguments that are known. Can be called several times in a row to
4395 * collect all options in between other arguments.
4396 * Return a pointer to the next argument (which isn't an option).
4397 * Return NULL for any error;
4398 */
4399 static char_u *
Bram Moolenaar860cae12010-06-05 23:22:07 +02004400get_syn_options(arg, opt, conceal_char)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004401 char_u *arg; /* next argument to be checked */
4402 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004403 int *conceal_char UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004404{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004405 char_u *gname_start, *gname;
4406 int syn_id;
4407 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004408 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004409 int i;
4410 int fidx;
4411 static struct flag
4412 {
4413 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004414 int argtype;
4415 int flags;
4416 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4417 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4418 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4419 {"eExXtTeEnNdD", 0, HL_EXTEND},
4420 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4421 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4422 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4423 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4424 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4425 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4426 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4427 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4428 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004429 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4430 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4431 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004432 {"cCoOnNtTaAiInNsS", 1, 0},
4433 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4434 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004435 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004436 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004437
4438 if (arg == NULL) /* already detected error */
4439 return NULL;
4440
Bram Moolenaar860cae12010-06-05 23:22:07 +02004441#ifdef FEAT_CONCEAL
4442 if (curwin->w_s->b_syn_conceal)
4443 opt->flags |= HL_CONCEAL;
4444#endif
4445
Bram Moolenaar071d4272004-06-13 20:20:40 +00004446 for (;;)
4447 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004448 /*
4449 * This is used very often when a large number of keywords is defined.
4450 * Need to skip quickly when no option name is found.
4451 * Also avoid tolower(), it's slow.
4452 */
4453 if (strchr(first_letters, *arg) == NULL)
4454 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004455
4456 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4457 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004458 p = flagtab[fidx].name;
4459 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4460 if (arg[len] != p[i] && arg[len] != p[i + 1])
4461 break;
4462 if (p[i] == NUL && (vim_iswhite(arg[len])
4463 || (flagtab[fidx].argtype > 0
4464 ? arg[len] == '='
4465 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004466 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004467 if (opt->keyword
4468 && (flagtab[fidx].flags == HL_DISPLAY
4469 || flagtab[fidx].flags == HL_FOLD
4470 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004471 /* treat "display", "fold" and "extend" as a keyword */
4472 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004473 break;
4474 }
4475 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004476 if (fidx < 0) /* no match found */
4477 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004478
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004479 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004480 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004481 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004482 {
4483 EMSG(_("E395: contains argument not accepted here"));
4484 return NULL;
4485 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004486 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004487 return NULL;
4488 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004489 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004490 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004491#if 0 /* cannot happen */
4492 if (opt->cont_in_list == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004493 {
4494 EMSG(_("E396: containedin argument not accepted here"));
4495 return NULL;
4496 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004497#endif
4498 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004499 return NULL;
4500 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004501 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004502 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004503 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004504 return NULL;
4505 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004506 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4507 {
4508#ifdef FEAT_MBYTE
4509 /* cchar=? */
4510 if (has_mbyte)
4511 {
4512# ifdef FEAT_CONCEAL
4513 *conceal_char = mb_ptr2char(arg + 6);
4514# endif
4515 arg += mb_ptr2len(arg + 6) - 1;
4516 }
4517 else
4518#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004519 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004520#ifdef FEAT_CONCEAL
4521 *conceal_char = arg[6];
4522#else
4523 ;
4524#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004525 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004526 arg = skipwhite(arg + 7);
4527 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004528 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004529 {
4530 opt->flags |= flagtab[fidx].flags;
4531 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004532
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004533 if (flagtab[fidx].flags == HL_SYNC_HERE
4534 || flagtab[fidx].flags == HL_SYNC_THERE)
4535 {
4536 if (opt->sync_idx == NULL)
4537 {
4538 EMSG(_("E393: group[t]here not accepted here"));
4539 return NULL;
4540 }
4541 gname_start = arg;
4542 arg = skiptowhite(arg);
4543 if (gname_start == arg)
4544 return NULL;
4545 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4546 if (gname == NULL)
4547 return NULL;
4548 if (STRCMP(gname, "NONE") == 0)
4549 *opt->sync_idx = NONE_IDX;
4550 else
4551 {
4552 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004553 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4554 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4555 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004556 {
4557 *opt->sync_idx = i;
4558 break;
4559 }
4560 if (i < 0)
4561 {
4562 EMSG2(_("E394: Didn't find region item for %s"), gname);
4563 vim_free(gname);
4564 return NULL;
4565 }
4566 }
4567
4568 vim_free(gname);
4569 arg = skipwhite(arg);
4570 }
4571#ifdef FEAT_FOLDING
4572 else if (flagtab[fidx].flags == HL_FOLD
4573 && foldmethodIsSyntax(curwin))
4574 /* Need to update folds later. */
4575 foldUpdateAll(curwin);
4576#endif
4577 }
4578 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004579
4580 return arg;
4581}
4582
4583/*
4584 * Adjustments to syntax item when declared in a ":syn include"'d file.
4585 * Set the contained flag, and if the item is not already contained, add it
4586 * to the specified top-level group, if any.
4587 */
4588 static void
4589syn_incl_toplevel(id, flagsp)
4590 int id;
4591 int *flagsp;
4592{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004593 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004594 return;
4595 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004596 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004597 {
4598 /* We have to alloc this, because syn_combine_list() will free it. */
4599 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004600 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004601
4602 if (grp_list != NULL)
4603 {
4604 grp_list[0] = id;
4605 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004606 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004607 CLUSTER_ADD);
4608 }
4609 }
4610}
4611
4612/*
4613 * Handle ":syntax include [@{group-name}] filename" command.
4614 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004615 static void
4616syn_cmd_include(eap, syncing)
4617 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004618 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004619{
4620 char_u *arg = eap->arg;
4621 int sgl_id = 1;
4622 char_u *group_name_end;
4623 char_u *rest;
4624 char_u *errormsg = NULL;
4625 int prev_toplvl_grp;
4626 int prev_syn_inc_tag;
4627 int source = FALSE;
4628
4629 eap->nextcmd = find_nextcmd(arg);
4630 if (eap->skip)
4631 return;
4632
4633 if (arg[0] == '@')
4634 {
4635 ++arg;
4636 rest = get_group_name(arg, &group_name_end);
4637 if (rest == NULL)
4638 {
4639 EMSG((char_u *)_("E397: Filename required"));
4640 return;
4641 }
4642 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4643 /* separate_nextcmd() and expand_filename() depend on this */
4644 eap->arg = rest;
4645 }
4646
4647 /*
4648 * Everything that's left, up to the next command, should be the
4649 * filename to include.
4650 */
4651 eap->argt |= (XFILE | NOSPC);
4652 separate_nextcmd(eap);
4653 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4654 {
4655 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4656 * file. Need to expand the file name first. In other cases
4657 * ":runtime!" is used. */
4658 source = TRUE;
4659 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4660 {
4661 if (errormsg != NULL)
4662 EMSG(errormsg);
4663 return;
4664 }
4665 }
4666
4667 /*
4668 * Save and restore the existing top-level grouplist id and ":syn
4669 * include" tag around the actual inclusion.
4670 */
4671 prev_syn_inc_tag = current_syn_inc_tag;
4672 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004673 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4674 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004675 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4676 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004677 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004678 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004679 current_syn_inc_tag = prev_syn_inc_tag;
4680}
4681
4682/*
4683 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4684 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004685 static void
4686syn_cmd_keyword(eap, syncing)
4687 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004688 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004689{
4690 char_u *arg = eap->arg;
4691 char_u *group_name_end;
4692 int syn_id;
4693 char_u *rest;
4694 char_u *keyword_copy;
4695 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004696 char_u *kw;
4697 syn_opt_arg_T syn_opt_arg;
4698 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004699 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004700
4701 rest = get_group_name(arg, &group_name_end);
4702
4703 if (rest != NULL)
4704 {
4705 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4706
4707 /* allocate a buffer, for removing the backslashes in the keyword */
4708 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4709 if (keyword_copy != NULL)
4710 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004711 syn_opt_arg.flags = 0;
4712 syn_opt_arg.keyword = TRUE;
4713 syn_opt_arg.sync_idx = NULL;
4714 syn_opt_arg.has_cont_list = FALSE;
4715 syn_opt_arg.cont_in_list = NULL;
4716 syn_opt_arg.next_list = NULL;
4717
Bram Moolenaar071d4272004-06-13 20:20:40 +00004718 /*
4719 * The options given apply to ALL keywords, so all options must be
4720 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004721 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004722 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004723 cnt = 0;
4724 p = keyword_copy;
4725 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004726 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004727 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004728 if (rest == NULL || ends_excmd(*rest))
4729 break;
4730 /* Copy the keyword, removing backslashes, and add a NUL. */
4731 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004732 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004733 if (*rest == '\\' && rest[1] != NUL)
4734 ++rest;
4735 *p++ = *rest++;
4736 }
4737 *p++ = NUL;
4738 ++cnt;
4739 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004740
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004741 if (!eap->skip)
4742 {
4743 /* Adjust flags for use of ":syn include". */
4744 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4745
4746 /*
4747 * 2: Add an entry for each keyword.
4748 */
4749 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4750 {
4751 for (p = vim_strchr(kw, '['); ; )
4752 {
4753 if (p != NULL)
4754 *p = NUL;
4755 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004756 syn_opt_arg.cont_in_list,
4757 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004758 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004759 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004760 if (p[1] == NUL)
4761 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004762 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004763 kw = p + 2; /* skip over the NUL */
4764 break;
4765 }
4766 if (p[1] == ']')
4767 {
4768 kw = p + 1; /* skip over the "]" */
4769 break;
4770 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004771#ifdef FEAT_MBYTE
4772 if (has_mbyte)
4773 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004774 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004775
4776 mch_memmove(p, p + 1, l);
4777 p += l;
4778 }
4779 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004780#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004781 {
4782 p[0] = p[1];
4783 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004784 }
4785 }
4786 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004787 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004788
Bram Moolenaar071d4272004-06-13 20:20:40 +00004789 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004790 vim_free(syn_opt_arg.cont_in_list);
4791 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004792 }
4793 }
4794
4795 if (rest != NULL)
4796 eap->nextcmd = check_nextcmd(rest);
4797 else
4798 EMSG2(_(e_invarg2), arg);
4799
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004800 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004801 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004802}
4803
4804/*
4805 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4806 *
4807 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4808 */
4809 static void
4810syn_cmd_match(eap, syncing)
4811 exarg_T *eap;
4812 int syncing; /* TRUE for ":syntax sync match .. " */
4813{
4814 char_u *arg = eap->arg;
4815 char_u *group_name_end;
4816 char_u *rest;
4817 synpat_T item; /* the item found in the line */
4818 int syn_id;
4819 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004820 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004821 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004822 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004823
4824 /* Isolate the group name, check for validity */
4825 rest = get_group_name(arg, &group_name_end);
4826
4827 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004828 syn_opt_arg.flags = 0;
4829 syn_opt_arg.keyword = FALSE;
4830 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4831 syn_opt_arg.has_cont_list = TRUE;
4832 syn_opt_arg.cont_list = NULL;
4833 syn_opt_arg.cont_in_list = NULL;
4834 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004835 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004836
4837 /* get the pattern. */
4838 init_syn_patterns();
4839 vim_memset(&item, 0, sizeof(item));
4840 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004841 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4842 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004843
4844 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004845 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004846
4847 if (rest != NULL) /* all arguments are valid */
4848 {
4849 /*
4850 * Check for trailing command and illegal trailing arguments.
4851 */
4852 eap->nextcmd = check_nextcmd(rest);
4853 if (!ends_excmd(*rest) || eap->skip)
4854 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004855 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004856 && (syn_id = syn_check_group(arg,
4857 (int)(group_name_end - arg))) != 0)
4858 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004859 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004860 /*
4861 * Store the pattern in the syn_items list
4862 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004863 idx = curwin->w_s->b_syn_patterns.ga_len;
4864 SYN_ITEMS(curwin->w_s)[idx] = item;
4865 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4866 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4867 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4868 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4869 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4870 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4871 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4872 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004873 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004874#ifdef FEAT_CONCEAL
4875 SYN_ITEMS(curwin->w_s)[idx].sp_char = conceal_char;
4876#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004877 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004878 curwin->w_s->b_syn_containedin = TRUE;
4879 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4880 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004881
4882 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004883 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004884 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004885#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004886 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004887 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004888#endif
4889
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004890 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004891 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004892 return; /* don't free the progs and patterns now */
4893 }
4894 }
4895
4896 /*
4897 * Something failed, free the allocated memory.
4898 */
4899 vim_free(item.sp_prog);
4900 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004901 vim_free(syn_opt_arg.cont_list);
4902 vim_free(syn_opt_arg.cont_in_list);
4903 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004904
4905 if (rest == NULL)
4906 EMSG2(_(e_invarg2), arg);
4907}
4908
4909/*
4910 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4911 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4912 */
4913 static void
4914syn_cmd_region(eap, syncing)
4915 exarg_T *eap;
4916 int syncing; /* TRUE for ":syntax sync region .." */
4917{
4918 char_u *arg = eap->arg;
4919 char_u *group_name_end;
4920 char_u *rest; /* next arg, NULL on error */
4921 char_u *key_end;
4922 char_u *key = NULL;
4923 char_u *p;
4924 int item;
4925#define ITEM_START 0
4926#define ITEM_SKIP 1
4927#define ITEM_END 2
4928#define ITEM_MATCHGROUP 3
4929 struct pat_ptr
4930 {
4931 synpat_T *pp_synp; /* pointer to syn_pattern */
4932 int pp_matchgroup_id; /* matchgroup ID */
4933 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4934 } *(pat_ptrs[3]);
4935 /* patterns found in the line */
4936 struct pat_ptr *ppp;
4937 struct pat_ptr *ppp_next;
4938 int pat_count = 0; /* nr of syn_patterns found */
4939 int syn_id;
4940 int matchgroup_id = 0;
4941 int not_enough = FALSE; /* not enough arguments */
4942 int illegal = FALSE; /* illegal arguments */
4943 int success = FALSE;
4944 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004945 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004946 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004947
4948 /* Isolate the group name, check for validity */
4949 rest = get_group_name(arg, &group_name_end);
4950
4951 pat_ptrs[0] = NULL;
4952 pat_ptrs[1] = NULL;
4953 pat_ptrs[2] = NULL;
4954
4955 init_syn_patterns();
4956
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004957 syn_opt_arg.flags = 0;
4958 syn_opt_arg.keyword = FALSE;
4959 syn_opt_arg.sync_idx = NULL;
4960 syn_opt_arg.has_cont_list = TRUE;
4961 syn_opt_arg.cont_list = NULL;
4962 syn_opt_arg.cont_in_list = NULL;
4963 syn_opt_arg.next_list = NULL;
4964
Bram Moolenaar071d4272004-06-13 20:20:40 +00004965 /*
4966 * get the options, patterns and matchgroup.
4967 */
4968 while (rest != NULL && !ends_excmd(*rest))
4969 {
4970 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004971 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004972 if (rest == NULL || ends_excmd(*rest))
4973 break;
4974
4975 /* must be a pattern or matchgroup then */
4976 key_end = rest;
4977 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4978 ++key_end;
4979 vim_free(key);
4980 key = vim_strnsave_up(rest, (int)(key_end - rest));
4981 if (key == NULL) /* out of memory */
4982 {
4983 rest = NULL;
4984 break;
4985 }
4986 if (STRCMP(key, "MATCHGROUP") == 0)
4987 item = ITEM_MATCHGROUP;
4988 else if (STRCMP(key, "START") == 0)
4989 item = ITEM_START;
4990 else if (STRCMP(key, "END") == 0)
4991 item = ITEM_END;
4992 else if (STRCMP(key, "SKIP") == 0)
4993 {
4994 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
4995 {
4996 illegal = TRUE;
4997 break;
4998 }
4999 item = ITEM_SKIP;
5000 }
5001 else
5002 break;
5003 rest = skipwhite(key_end);
5004 if (*rest != '=')
5005 {
5006 rest = NULL;
5007 EMSG2(_("E398: Missing '=': %s"), arg);
5008 break;
5009 }
5010 rest = skipwhite(rest + 1);
5011 if (*rest == NUL)
5012 {
5013 not_enough = TRUE;
5014 break;
5015 }
5016
5017 if (item == ITEM_MATCHGROUP)
5018 {
5019 p = skiptowhite(rest);
5020 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5021 matchgroup_id = 0;
5022 else
5023 {
5024 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5025 if (matchgroup_id == 0)
5026 {
5027 illegal = TRUE;
5028 break;
5029 }
5030 }
5031 rest = skipwhite(p);
5032 }
5033 else
5034 {
5035 /*
5036 * Allocate room for a syn_pattern, and link it in the list of
5037 * syn_patterns for this item, at the start (because the list is
5038 * used from end to start).
5039 */
5040 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5041 if (ppp == NULL)
5042 {
5043 rest = NULL;
5044 break;
5045 }
5046 ppp->pp_next = pat_ptrs[item];
5047 pat_ptrs[item] = ppp;
5048 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5049 if (ppp->pp_synp == NULL)
5050 {
5051 rest = NULL;
5052 break;
5053 }
5054
5055 /*
5056 * Get the syntax pattern and the following offset(s).
5057 */
5058 /* Enable the appropriate \z specials. */
5059 if (item == ITEM_START)
5060 reg_do_extmatch = REX_SET;
5061 else if (item == ITEM_SKIP || item == ITEM_END)
5062 reg_do_extmatch = REX_USE;
5063 rest = get_syn_pattern(rest, ppp->pp_synp);
5064 reg_do_extmatch = 0;
5065 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005066 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005067 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5068 ppp->pp_matchgroup_id = matchgroup_id;
5069 ++pat_count;
5070 }
5071 }
5072 vim_free(key);
5073 if (illegal || not_enough)
5074 rest = NULL;
5075
5076 /*
5077 * Must have a "start" and "end" pattern.
5078 */
5079 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5080 pat_ptrs[ITEM_END] == NULL))
5081 {
5082 not_enough = TRUE;
5083 rest = NULL;
5084 }
5085
5086 if (rest != NULL)
5087 {
5088 /*
5089 * Check for trailing garbage or command.
5090 * If OK, add the item.
5091 */
5092 eap->nextcmd = check_nextcmd(rest);
5093 if (!ends_excmd(*rest) || eap->skip)
5094 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005095 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005096 && (syn_id = syn_check_group(arg,
5097 (int)(group_name_end - arg))) != 0)
5098 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005099 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005100 /*
5101 * Store the start/skip/end in the syn_items list
5102 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005103 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005104 for (item = ITEM_START; item <= ITEM_END; ++item)
5105 {
5106 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5107 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005108 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5109 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5110 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005111 (item == ITEM_START) ? SPTYPE_START :
5112 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005113 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5114 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5115 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5116 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005117 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005118#ifdef FEAT_CONCEAL
5119 SYN_ITEMS(curwin->w_s)[idx].sp_char = conceal_char;
5120#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005121 if (item == ITEM_START)
5122 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005123 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005124 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005125 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005126 syn_opt_arg.cont_in_list;
5127 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005128 curwin->w_s->b_syn_containedin = TRUE;
5129 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005130 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005131 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005132 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005133 ++idx;
5134#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005135 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005136 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005137#endif
5138 }
5139 }
5140
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005141 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005142 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005143 success = TRUE; /* don't free the progs and patterns now */
5144 }
5145 }
5146
5147 /*
5148 * Free the allocated memory.
5149 */
5150 for (item = ITEM_START; item <= ITEM_END; ++item)
5151 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5152 {
5153 if (!success)
5154 {
5155 vim_free(ppp->pp_synp->sp_prog);
5156 vim_free(ppp->pp_synp->sp_pattern);
5157 }
5158 vim_free(ppp->pp_synp);
5159 ppp_next = ppp->pp_next;
5160 vim_free(ppp);
5161 }
5162
5163 if (!success)
5164 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005165 vim_free(syn_opt_arg.cont_list);
5166 vim_free(syn_opt_arg.cont_in_list);
5167 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005168 if (not_enough)
5169 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5170 else if (illegal || rest == NULL)
5171 EMSG2(_(e_invarg2), arg);
5172 }
5173}
5174
5175/*
5176 * A simple syntax group ID comparison function suitable for use in qsort()
5177 */
5178 static int
5179#ifdef __BORLANDC__
5180_RTLENTRYF
5181#endif
5182syn_compare_stub(v1, v2)
5183 const void *v1;
5184 const void *v2;
5185{
5186 const short *s1 = v1;
5187 const short *s2 = v2;
5188
5189 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5190}
5191
5192/*
5193 * Combines lists of syntax clusters.
5194 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5195 */
5196 static void
5197syn_combine_list(clstr1, clstr2, list_op)
5198 short **clstr1;
5199 short **clstr2;
5200 int list_op;
5201{
5202 int count1 = 0;
5203 int count2 = 0;
5204 short *g1;
5205 short *g2;
5206 short *clstr = NULL;
5207 int count;
5208 int round;
5209
5210 /*
5211 * Handle degenerate cases.
5212 */
5213 if (*clstr2 == NULL)
5214 return;
5215 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5216 {
5217 if (list_op == CLUSTER_REPLACE)
5218 vim_free(*clstr1);
5219 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5220 *clstr1 = *clstr2;
5221 else
5222 vim_free(*clstr2);
5223 return;
5224 }
5225
5226 for (g1 = *clstr1; *g1; g1++)
5227 ++count1;
5228 for (g2 = *clstr2; *g2; g2++)
5229 ++count2;
5230
5231 /*
5232 * For speed purposes, sort both lists.
5233 */
5234 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5235 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5236
5237 /*
5238 * We proceed in two passes; in round 1, we count the elements to place
5239 * in the new list, and in round 2, we allocate and populate the new
5240 * list. For speed, we use a mergesort-like method, adding the smaller
5241 * of the current elements in each list to the new list.
5242 */
5243 for (round = 1; round <= 2; round++)
5244 {
5245 g1 = *clstr1;
5246 g2 = *clstr2;
5247 count = 0;
5248
5249 /*
5250 * First, loop through the lists until one of them is empty.
5251 */
5252 while (*g1 && *g2)
5253 {
5254 /*
5255 * We always want to add from the first list.
5256 */
5257 if (*g1 < *g2)
5258 {
5259 if (round == 2)
5260 clstr[count] = *g1;
5261 count++;
5262 g1++;
5263 continue;
5264 }
5265 /*
5266 * We only want to add from the second list if we're adding the
5267 * lists.
5268 */
5269 if (list_op == CLUSTER_ADD)
5270 {
5271 if (round == 2)
5272 clstr[count] = *g2;
5273 count++;
5274 }
5275 if (*g1 == *g2)
5276 g1++;
5277 g2++;
5278 }
5279
5280 /*
5281 * Now add the leftovers from whichever list didn't get finished
5282 * first. As before, we only want to add from the second list if
5283 * we're adding the lists.
5284 */
5285 for (; *g1; g1++, count++)
5286 if (round == 2)
5287 clstr[count] = *g1;
5288 if (list_op == CLUSTER_ADD)
5289 for (; *g2; g2++, count++)
5290 if (round == 2)
5291 clstr[count] = *g2;
5292
5293 if (round == 1)
5294 {
5295 /*
5296 * If the group ended up empty, we don't need to allocate any
5297 * space for it.
5298 */
5299 if (count == 0)
5300 {
5301 clstr = NULL;
5302 break;
5303 }
5304 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5305 if (clstr == NULL)
5306 break;
5307 clstr[count] = 0;
5308 }
5309 }
5310
5311 /*
5312 * Finally, put the new list in place.
5313 */
5314 vim_free(*clstr1);
5315 vim_free(*clstr2);
5316 *clstr1 = clstr;
5317}
5318
5319/*
5320 * Lookup a syntax cluster name and return it's ID.
5321 * If it is not found, 0 is returned.
5322 */
5323 static int
5324syn_scl_name2id(name)
5325 char_u *name;
5326{
5327 int i;
5328 char_u *name_u;
5329
5330 /* Avoid using stricmp() too much, it's slow on some systems */
5331 name_u = vim_strsave_up(name);
5332 if (name_u == NULL)
5333 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005334 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5335 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5336 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005337 break;
5338 vim_free(name_u);
5339 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5340}
5341
5342/*
5343 * Like syn_scl_name2id(), but take a pointer + length argument.
5344 */
5345 static int
5346syn_scl_namen2id(linep, len)
5347 char_u *linep;
5348 int len;
5349{
5350 char_u *name;
5351 int id = 0;
5352
5353 name = vim_strnsave(linep, len);
5354 if (name != NULL)
5355 {
5356 id = syn_scl_name2id(name);
5357 vim_free(name);
5358 }
5359 return id;
5360}
5361
5362/*
5363 * Find syntax cluster name in the table and return it's ID.
5364 * The argument is a pointer to the name and the length of the name.
5365 * If it doesn't exist yet, a new entry is created.
5366 * Return 0 for failure.
5367 */
5368 static int
5369syn_check_cluster(pp, len)
5370 char_u *pp;
5371 int len;
5372{
5373 int id;
5374 char_u *name;
5375
5376 name = vim_strnsave(pp, len);
5377 if (name == NULL)
5378 return 0;
5379
5380 id = syn_scl_name2id(name);
5381 if (id == 0) /* doesn't exist yet */
5382 id = syn_add_cluster(name);
5383 else
5384 vim_free(name);
5385 return id;
5386}
5387
5388/*
5389 * Add new syntax cluster and return it's ID.
5390 * "name" must be an allocated string, it will be consumed.
5391 * Return 0 for failure.
5392 */
5393 static int
5394syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005395 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005396{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005397 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005398
5399 /*
5400 * First call for this growarray: init growing array.
5401 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005402 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005403 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005404 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5405 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005406 }
5407
5408 /*
5409 * Make room for at least one other cluster entry.
5410 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005411 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005412 {
5413 vim_free(name);
5414 return 0;
5415 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005416 len = curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005417
Bram Moolenaar860cae12010-06-05 23:22:07 +02005418 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5419 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5420 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5421 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5422 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005423
Bram Moolenaar217ad922005-03-20 22:37:15 +00005424 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005425 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005426 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005427 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005428
Bram Moolenaar071d4272004-06-13 20:20:40 +00005429 return len + SYNID_CLUSTER;
5430}
5431
5432/*
5433 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5434 * [add={groupname},..] [remove={groupname},..]".
5435 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005436 static void
5437syn_cmd_cluster(eap, syncing)
5438 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005439 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005440{
5441 char_u *arg = eap->arg;
5442 char_u *group_name_end;
5443 char_u *rest;
5444 int scl_id;
5445 short *clstr_list;
5446 int got_clstr = FALSE;
5447 int opt_len;
5448 int list_op;
5449
5450 eap->nextcmd = find_nextcmd(arg);
5451 if (eap->skip)
5452 return;
5453
5454 rest = get_group_name(arg, &group_name_end);
5455
5456 if (rest != NULL)
5457 {
5458 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
Bram Moolenaar217ad922005-03-20 22:37:15 +00005459 - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005460
5461 for (;;)
5462 {
5463 if (STRNICMP(rest, "add", 3) == 0
5464 && (vim_iswhite(rest[3]) || rest[3] == '='))
5465 {
5466 opt_len = 3;
5467 list_op = CLUSTER_ADD;
5468 }
5469 else if (STRNICMP(rest, "remove", 6) == 0
5470 && (vim_iswhite(rest[6]) || rest[6] == '='))
5471 {
5472 opt_len = 6;
5473 list_op = CLUSTER_SUBTRACT;
5474 }
5475 else if (STRNICMP(rest, "contains", 8) == 0
5476 && (vim_iswhite(rest[8]) || rest[8] == '='))
5477 {
5478 opt_len = 8;
5479 list_op = CLUSTER_REPLACE;
5480 }
5481 else
5482 break;
5483
5484 clstr_list = NULL;
5485 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5486 {
5487 EMSG2(_(e_invarg2), rest);
5488 break;
5489 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005490 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005491 &clstr_list, list_op);
5492 got_clstr = TRUE;
5493 }
5494
5495 if (got_clstr)
5496 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005497 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005498 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005499 }
5500 }
5501
5502 if (!got_clstr)
5503 EMSG(_("E400: No cluster specified"));
5504 if (rest == NULL || !ends_excmd(*rest))
5505 EMSG2(_(e_invarg2), arg);
5506}
5507
5508/*
5509 * On first call for current buffer: Init growing array.
5510 */
5511 static void
5512init_syn_patterns()
5513{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005514 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5515 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005516}
5517
5518/*
5519 * Get one pattern for a ":syntax match" or ":syntax region" command.
5520 * Stores the pattern and program in a synpat_T.
5521 * Returns a pointer to the next argument, or NULL in case of an error.
5522 */
5523 static char_u *
5524get_syn_pattern(arg, ci)
5525 char_u *arg;
5526 synpat_T *ci;
5527{
5528 char_u *end;
5529 int *p;
5530 int idx;
5531 char_u *cpo_save;
5532
5533 /* need at least three chars */
5534 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5535 return NULL;
5536
5537 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5538 if (*end != *arg) /* end delimiter not found */
5539 {
5540 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5541 return NULL;
5542 }
5543 /* store the pattern and compiled regexp program */
5544 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5545 return NULL;
5546
5547 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5548 cpo_save = p_cpo;
5549 p_cpo = (char_u *)"";
5550 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5551 p_cpo = cpo_save;
5552
5553 if (ci->sp_prog == NULL)
5554 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005555 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005556
5557 /*
5558 * Check for a match, highlight or region offset.
5559 */
5560 ++end;
5561 do
5562 {
5563 for (idx = SPO_COUNT; --idx >= 0; )
5564 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5565 break;
5566 if (idx >= 0)
5567 {
5568 p = &(ci->sp_offsets[idx]);
5569 if (idx != SPO_LC_OFF)
5570 switch (end[3])
5571 {
5572 case 's': break;
5573 case 'b': break;
5574 case 'e': idx += SPO_COUNT; break;
5575 default: idx = -1; break;
5576 }
5577 if (idx >= 0)
5578 {
5579 ci->sp_off_flags |= (1 << idx);
5580 if (idx == SPO_LC_OFF) /* lc=99 */
5581 {
5582 end += 3;
5583 *p = getdigits(&end);
5584
5585 /* "lc=" offset automatically sets "ms=" offset */
5586 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5587 {
5588 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5589 ci->sp_offsets[SPO_MS_OFF] = *p;
5590 }
5591 }
5592 else /* yy=x+99 */
5593 {
5594 end += 4;
5595 if (*end == '+')
5596 {
5597 ++end;
5598 *p = getdigits(&end); /* positive offset */
5599 }
5600 else if (*end == '-')
5601 {
5602 ++end;
5603 *p = -getdigits(&end); /* negative offset */
5604 }
5605 }
5606 if (*end != ',')
5607 break;
5608 ++end;
5609 }
5610 }
5611 } while (idx >= 0);
5612
5613 if (!ends_excmd(*end) && !vim_iswhite(*end))
5614 {
5615 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5616 return NULL;
5617 }
5618 return skipwhite(end);
5619}
5620
5621/*
5622 * Handle ":syntax sync .." command.
5623 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005624 static void
5625syn_cmd_sync(eap, syncing)
5626 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005627 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005628{
5629 char_u *arg_start = eap->arg;
5630 char_u *arg_end;
5631 char_u *key = NULL;
5632 char_u *next_arg;
5633 int illegal = FALSE;
5634 int finished = FALSE;
5635 long n;
5636 char_u *cpo_save;
5637
5638 if (ends_excmd(*arg_start))
5639 {
5640 syn_cmd_list(eap, TRUE);
5641 return;
5642 }
5643
5644 while (!ends_excmd(*arg_start))
5645 {
5646 arg_end = skiptowhite(arg_start);
5647 next_arg = skipwhite(arg_end);
5648 vim_free(key);
5649 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5650 if (STRCMP(key, "CCOMMENT") == 0)
5651 {
5652 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005653 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005654 if (!ends_excmd(*next_arg))
5655 {
5656 arg_end = skiptowhite(next_arg);
5657 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005658 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005659 (int)(arg_end - next_arg));
5660 next_arg = skipwhite(arg_end);
5661 }
5662 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005663 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005664 }
5665 else if ( STRNCMP(key, "LINES", 5) == 0
5666 || STRNCMP(key, "MINLINES", 8) == 0
5667 || STRNCMP(key, "MAXLINES", 8) == 0
5668 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5669 {
5670 if (key[4] == 'S')
5671 arg_end = key + 6;
5672 else if (key[0] == 'L')
5673 arg_end = key + 11;
5674 else
5675 arg_end = key + 9;
5676 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5677 {
5678 illegal = TRUE;
5679 break;
5680 }
5681 n = getdigits(&arg_end);
5682 if (!eap->skip)
5683 {
5684 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005685 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005686 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005687 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005688 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005689 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005690 }
5691 }
5692 else if (STRCMP(key, "FROMSTART") == 0)
5693 {
5694 if (!eap->skip)
5695 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005696 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5697 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005698 }
5699 }
5700 else if (STRCMP(key, "LINECONT") == 0)
5701 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005702 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005703 {
5704 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5705 finished = TRUE;
5706 break;
5707 }
5708 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5709 if (*arg_end != *next_arg) /* end delimiter not found */
5710 {
5711 illegal = TRUE;
5712 break;
5713 }
5714
5715 if (!eap->skip)
5716 {
5717 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005718 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005719 (int)(arg_end - next_arg - 1))) == NULL)
5720 {
5721 finished = TRUE;
5722 break;
5723 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005724 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005725
5726 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5727 cpo_save = p_cpo;
5728 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005729 curwin->w_s->b_syn_linecont_prog =
5730 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005731 p_cpo = cpo_save;
5732
Bram Moolenaar860cae12010-06-05 23:22:07 +02005733 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005734 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005735 vim_free(curwin->w_s->b_syn_linecont_pat);
5736 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005737 finished = TRUE;
5738 break;
5739 }
5740 }
5741 next_arg = skipwhite(arg_end + 1);
5742 }
5743 else
5744 {
5745 eap->arg = next_arg;
5746 if (STRCMP(key, "MATCH") == 0)
5747 syn_cmd_match(eap, TRUE);
5748 else if (STRCMP(key, "REGION") == 0)
5749 syn_cmd_region(eap, TRUE);
5750 else if (STRCMP(key, "CLEAR") == 0)
5751 syn_cmd_clear(eap, TRUE);
5752 else
5753 illegal = TRUE;
5754 finished = TRUE;
5755 break;
5756 }
5757 arg_start = next_arg;
5758 }
5759 vim_free(key);
5760 if (illegal)
5761 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5762 else if (!finished)
5763 {
5764 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005765 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005766 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005767 }
5768}
5769
5770/*
5771 * Convert a line of highlight group names into a list of group ID numbers.
5772 * "arg" should point to the "contains" or "nextgroup" keyword.
5773 * "arg" is advanced to after the last group name.
5774 * Careful: the argument is modified (NULs added).
5775 * returns FAIL for some error, OK for success.
5776 */
5777 static int
5778get_id_list(arg, keylen, list)
5779 char_u **arg;
5780 int keylen; /* length of keyword */
5781 short **list; /* where to store the resulting list, if not
5782 NULL, the list is silently skipped! */
5783{
5784 char_u *p = NULL;
5785 char_u *end;
5786 int round;
5787 int count;
5788 int total_count = 0;
5789 short *retval = NULL;
5790 char_u *name;
5791 regmatch_T regmatch;
5792 int id;
5793 int i;
5794 int failed = FALSE;
5795
5796 /*
5797 * We parse the list twice:
5798 * round == 1: count the number of items, allocate the array.
5799 * round == 2: fill the array with the items.
5800 * In round 1 new groups may be added, causing the number of items to
5801 * grow when a regexp is used. In that case round 1 is done once again.
5802 */
5803 for (round = 1; round <= 2; ++round)
5804 {
5805 /*
5806 * skip "contains"
5807 */
5808 p = skipwhite(*arg + keylen);
5809 if (*p != '=')
5810 {
5811 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5812 break;
5813 }
5814 p = skipwhite(p + 1);
5815 if (ends_excmd(*p))
5816 {
5817 EMSG2(_("E406: Empty argument: %s"), *arg);
5818 break;
5819 }
5820
5821 /*
5822 * parse the arguments after "contains"
5823 */
5824 count = 0;
5825 while (!ends_excmd(*p))
5826 {
5827 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5828 ;
5829 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5830 if (name == NULL)
5831 {
5832 failed = TRUE;
5833 break;
5834 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005835 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005836 if ( STRCMP(name + 1, "ALLBUT") == 0
5837 || STRCMP(name + 1, "ALL") == 0
5838 || STRCMP(name + 1, "TOP") == 0
5839 || STRCMP(name + 1, "CONTAINED") == 0)
5840 {
5841 if (TOUPPER_ASC(**arg) != 'C')
5842 {
5843 EMSG2(_("E407: %s not allowed here"), name + 1);
5844 failed = TRUE;
5845 vim_free(name);
5846 break;
5847 }
5848 if (count != 0)
5849 {
5850 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5851 failed = TRUE;
5852 vim_free(name);
5853 break;
5854 }
5855 if (name[1] == 'A')
5856 id = SYNID_ALLBUT;
5857 else if (name[1] == 'T')
5858 id = SYNID_TOP;
5859 else
5860 id = SYNID_CONTAINED;
5861 id += current_syn_inc_tag;
5862 }
5863 else if (name[1] == '@')
5864 {
5865 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5866 }
5867 else
5868 {
5869 /*
5870 * Handle full group name.
5871 */
5872 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5873 id = syn_check_group(name + 1, (int)(end - p));
5874 else
5875 {
5876 /*
5877 * Handle match of regexp with group names.
5878 */
5879 *name = '^';
5880 STRCAT(name, "$");
5881 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5882 if (regmatch.regprog == NULL)
5883 {
5884 failed = TRUE;
5885 vim_free(name);
5886 break;
5887 }
5888
5889 regmatch.rm_ic = TRUE;
5890 id = 0;
5891 for (i = highlight_ga.ga_len; --i >= 0; )
5892 {
5893 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5894 (colnr_T)0))
5895 {
5896 if (round == 2)
5897 {
5898 /* Got more items than expected; can happen
5899 * when adding items that match:
5900 * "contains=a.*b,axb".
5901 * Go back to first round */
5902 if (count >= total_count)
5903 {
5904 vim_free(retval);
5905 round = 1;
5906 }
5907 else
5908 retval[count] = i + 1;
5909 }
5910 ++count;
5911 id = -1; /* remember that we found one */
5912 }
5913 }
5914 vim_free(regmatch.regprog);
5915 }
5916 }
5917 vim_free(name);
5918 if (id == 0)
5919 {
5920 EMSG2(_("E409: Unknown group name: %s"), p);
5921 failed = TRUE;
5922 break;
5923 }
5924 if (id > 0)
5925 {
5926 if (round == 2)
5927 {
5928 /* Got more items than expected, go back to first round */
5929 if (count >= total_count)
5930 {
5931 vim_free(retval);
5932 round = 1;
5933 }
5934 else
5935 retval[count] = id;
5936 }
5937 ++count;
5938 }
5939 p = skipwhite(end);
5940 if (*p != ',')
5941 break;
5942 p = skipwhite(p + 1); /* skip comma in between arguments */
5943 }
5944 if (failed)
5945 break;
5946 if (round == 1)
5947 {
5948 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5949 if (retval == NULL)
5950 break;
5951 retval[count] = 0; /* zero means end of the list */
5952 total_count = count;
5953 }
5954 }
5955
5956 *arg = p;
5957 if (failed || retval == NULL)
5958 {
5959 vim_free(retval);
5960 return FAIL;
5961 }
5962
5963 if (*list == NULL)
5964 *list = retval;
5965 else
5966 vim_free(retval); /* list already found, don't overwrite it */
5967
5968 return OK;
5969}
5970
5971/*
5972 * Make a copy of an ID list.
5973 */
5974 static short *
5975copy_id_list(list)
5976 short *list;
5977{
5978 int len;
5979 int count;
5980 short *retval;
5981
5982 if (list == NULL)
5983 return NULL;
5984
5985 for (count = 0; list[count]; ++count)
5986 ;
5987 len = (count + 1) * sizeof(short);
5988 retval = (short *)alloc((unsigned)len);
5989 if (retval != NULL)
5990 mch_memmove(retval, list, (size_t)len);
5991
5992 return retval;
5993}
5994
5995/*
5996 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
5997 * "cur_si" can be NULL if not checking the "containedin" list.
5998 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
5999 * the current item.
6000 * This function is called very often, keep it fast!!
6001 */
6002 static int
6003in_id_list(cur_si, list, ssp, contained)
6004 stateitem_T *cur_si; /* current item or NULL */
6005 short *list; /* id list */
6006 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
6007 int contained; /* group id is contained */
6008{
6009 int retval;
6010 short *scl_list;
6011 short item;
6012 short id = ssp->id;
6013 static int depth = 0;
6014 int r;
6015
6016 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006017 if (cur_si != NULL && ssp->cont_in_list != NULL
6018 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006019 {
6020 /* Ignore transparent items without a contains argument. Double check
6021 * that we don't go back past the first one. */
6022 while ((cur_si->si_flags & HL_TRANS_CONT)
6023 && cur_si > (stateitem_T *)(current_state.ga_data))
6024 --cur_si;
6025 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6026 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006027 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6028 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006029 return TRUE;
6030 }
6031
6032 if (list == NULL)
6033 return FALSE;
6034
6035 /*
6036 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6037 * inside anything. Only allow not-contained groups.
6038 */
6039 if (list == ID_LIST_ALL)
6040 return !contained;
6041
6042 /*
6043 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6044 * contains list. We also require that "id" is at the same ":syn include"
6045 * level as the list.
6046 */
6047 item = *list;
6048 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6049 {
6050 if (item < SYNID_TOP)
6051 {
6052 /* ALL or ALLBUT: accept all groups in the same file */
6053 if (item - SYNID_ALLBUT != ssp->inc_tag)
6054 return FALSE;
6055 }
6056 else if (item < SYNID_CONTAINED)
6057 {
6058 /* TOP: accept all not-contained groups in the same file */
6059 if (item - SYNID_TOP != ssp->inc_tag || contained)
6060 return FALSE;
6061 }
6062 else
6063 {
6064 /* CONTAINED: accept all contained groups in the same file */
6065 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6066 return FALSE;
6067 }
6068 item = *++list;
6069 retval = FALSE;
6070 }
6071 else
6072 retval = TRUE;
6073
6074 /*
6075 * Return "retval" if id is in the contains list.
6076 */
6077 while (item != 0)
6078 {
6079 if (item == id)
6080 return retval;
6081 if (item >= SYNID_CLUSTER)
6082 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006083 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006084 /* restrict recursiveness to 30 to avoid an endless loop for a
6085 * cluster that includes itself (indirectly) */
6086 if (scl_list != NULL && depth < 30)
6087 {
6088 ++depth;
6089 r = in_id_list(NULL, scl_list, ssp, contained);
6090 --depth;
6091 if (r)
6092 return retval;
6093 }
6094 }
6095 item = *++list;
6096 }
6097 return !retval;
6098}
6099
6100struct subcommand
6101{
6102 char *name; /* subcommand name */
6103 void (*func)__ARGS((exarg_T *, int)); /* function to call */
6104};
6105
6106static struct subcommand subcommands[] =
6107{
6108 {"case", syn_cmd_case},
6109 {"clear", syn_cmd_clear},
6110 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006111 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006112 {"enable", syn_cmd_enable},
6113 {"include", syn_cmd_include},
6114 {"keyword", syn_cmd_keyword},
6115 {"list", syn_cmd_list},
6116 {"manual", syn_cmd_manual},
6117 {"match", syn_cmd_match},
6118 {"on", syn_cmd_on},
6119 {"off", syn_cmd_off},
6120 {"region", syn_cmd_region},
6121 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006122 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006123 {"sync", syn_cmd_sync},
6124 {"", syn_cmd_list},
6125 {NULL, NULL}
6126};
6127
6128/*
6129 * ":syntax".
6130 * This searches the subcommands[] table for the subcommand name, and calls a
6131 * syntax_subcommand() function to do the rest.
6132 */
6133 void
6134ex_syntax(eap)
6135 exarg_T *eap;
6136{
6137 char_u *arg = eap->arg;
6138 char_u *subcmd_end;
6139 char_u *subcmd_name;
6140 int i;
6141
6142 syn_cmdlinep = eap->cmdlinep;
6143
6144 /* isolate subcommand name */
6145 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6146 ;
6147 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6148 if (subcmd_name != NULL)
6149 {
6150 if (eap->skip) /* skip error messages for all subcommands */
6151 ++emsg_skip;
6152 for (i = 0; ; ++i)
6153 {
6154 if (subcommands[i].name == NULL)
6155 {
6156 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6157 break;
6158 }
6159 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6160 {
6161 eap->arg = skipwhite(subcmd_end);
6162 (subcommands[i].func)(eap, FALSE);
6163 break;
6164 }
6165 }
6166 vim_free(subcmd_name);
6167 if (eap->skip)
6168 --emsg_skip;
6169 }
6170}
6171
Bram Moolenaar860cae12010-06-05 23:22:07 +02006172 void
6173ex_ownsyntax(eap)
6174 exarg_T *eap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006175{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006176 char_u *old_value;
6177 char_u *new_value;
6178
Bram Moolenaar860cae12010-06-05 23:22:07 +02006179 if (curwin->w_s == &curwin->w_buffer->b_s)
6180 {
6181 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6182 memset(curwin->w_s, 0, sizeof(synblock_T));
6183#ifdef FEAT_SPELL
6184 curwin->w_p_spell = FALSE; /* No spell checking */
6185 clear_string_option(&curwin->w_s->b_p_spc);
6186 clear_string_option(&curwin->w_s->b_p_spf);
6187 vim_free(curwin->w_s->b_cap_prog);
6188 curwin->w_s->b_cap_prog = NULL;
6189 clear_string_option(&curwin->w_s->b_p_spl);
6190#endif
6191 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006192
6193 /* save value of b:current_syntax */
6194 old_value = get_var_value((char_u *)"b:current_syntax");
6195 if (old_value != NULL)
6196 old_value = vim_strsave(old_value);
6197
6198 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6199 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006200 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006201
6202 /* move value of b:current_syntax to w:current_syntax */
6203 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006204 if (new_value != NULL)
6205 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006206
6207 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006208 if (old_value == NULL)
6209 do_unlet((char_u *)"b:current_syntax", TRUE);
6210 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006211 {
6212 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6213 vim_free(old_value);
6214 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006215}
6216
6217 int
6218syntax_present(win)
6219 win_T *win;
6220{
6221 return (win->w_s->b_syn_patterns.ga_len != 0
6222 || win->w_s->b_syn_clusters.ga_len != 0
6223 || win->w_s->b_keywtab.ht_used > 0
6224 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006225}
6226
6227#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6228
6229static enum
6230{
6231 EXP_SUBCMD, /* expand ":syn" sub-commands */
6232 EXP_CASE /* expand ":syn case" arguments */
6233} expand_what;
6234
Bram Moolenaar4f688582007-07-24 12:34:30 +00006235/*
6236 * Reset include_link, include_default, include_none to 0.
6237 * Called when we are done expanding.
6238 */
6239 void
6240reset_expand_highlight()
6241{
6242 include_link = include_default = include_none = 0;
6243}
6244
6245/*
6246 * Handle command line completion for :match and :echohl command: Add "None"
6247 * as highlight group.
6248 */
6249 void
6250set_context_in_echohl_cmd(xp, arg)
6251 expand_T *xp;
6252 char_u *arg;
6253{
6254 xp->xp_context = EXPAND_HIGHLIGHT;
6255 xp->xp_pattern = arg;
6256 include_none = 1;
6257}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006258
6259/*
6260 * Handle command line completion for :syntax command.
6261 */
6262 void
6263set_context_in_syntax_cmd(xp, arg)
6264 expand_T *xp;
6265 char_u *arg;
6266{
6267 char_u *p;
6268
6269 /* Default: expand subcommands */
6270 xp->xp_context = EXPAND_SYNTAX;
6271 expand_what = EXP_SUBCMD;
6272 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006273 include_link = 0;
6274 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006275
6276 /* (part of) subcommand already typed */
6277 if (*arg != NUL)
6278 {
6279 p = skiptowhite(arg);
6280 if (*p != NUL) /* past first word */
6281 {
6282 xp->xp_pattern = skipwhite(p);
6283 if (*skiptowhite(xp->xp_pattern) != NUL)
6284 xp->xp_context = EXPAND_NOTHING;
6285 else if (STRNICMP(arg, "case", p - arg) == 0)
6286 expand_what = EXP_CASE;
6287 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6288 || STRNICMP(arg, "region", p - arg) == 0
6289 || STRNICMP(arg, "match", p - arg) == 0
6290 || STRNICMP(arg, "list", p - arg) == 0)
6291 xp->xp_context = EXPAND_HIGHLIGHT;
6292 else
6293 xp->xp_context = EXPAND_NOTHING;
6294 }
6295 }
6296}
6297
6298static char *(case_args[]) = {"match", "ignore", NULL};
6299
6300/*
6301 * Function given to ExpandGeneric() to obtain the list syntax names for
6302 * expansion.
6303 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006304 char_u *
6305get_syntax_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00006306 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006307 int idx;
6308{
6309 if (expand_what == EXP_SUBCMD)
6310 return (char_u *)subcommands[idx].name;
6311 return (char_u *)case_args[idx];
6312}
6313
6314#endif /* FEAT_CMDL_COMPL */
6315
Bram Moolenaar071d4272004-06-13 20:20:40 +00006316/*
6317 * Function called for expression evaluation: get syntax ID at file position.
6318 */
6319 int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006320syn_get_id(wp, lnum, col, trans, spellp, keep_state)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006321 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006322 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006323 colnr_T col;
Bram Moolenaarf5b63862009-12-16 17:13:44 +00006324 int trans; /* remove transparency */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006325 int *spellp; /* return: can do spell checking */
6326 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006327{
6328 /* When the position is not after the current position and in the same
6329 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006330 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006331 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006332 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006333 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006334
Bram Moolenaar860cae12010-06-05 23:22:07 +02006335 (void)get_syntax_attr(col, NULL, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006336
6337 return (trans ? current_trans_id : current_id);
6338}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006339
Bram Moolenaar860cae12010-06-05 23:22:07 +02006340#if defined(FEAT_CONCEAL) || defined(PROTO)
6341/*
6342 * Return conceal substitution character
6343 */
6344 int
6345syn_get_sub_char()
6346{
6347 return current_sub_char;
6348}
6349#endif
6350
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006351#if defined(FEAT_EVAL) || defined(PROTO)
6352/*
6353 * Return the syntax ID at position "i" in the current stack.
6354 * The caller must have called syn_get_id() before to fill the stack.
6355 * Returns -1 when "i" is out of range.
6356 */
6357 int
6358syn_get_stack_item(i)
6359 int i;
6360{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006361 if (i >= current_state.ga_len)
6362 {
6363 /* Need to invalidate the state, because we didn't properly finish it
6364 * for the last character, "keep_state" was TRUE. */
6365 invalidate_current_state();
6366 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006367 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006368 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006369 return CUR_STATE(i).si_id;
6370}
6371#endif
6372
Bram Moolenaar071d4272004-06-13 20:20:40 +00006373#if defined(FEAT_FOLDING) || defined(PROTO)
6374/*
6375 * Function called to get folding level for line "lnum" in window "wp".
6376 */
6377 int
6378syn_get_foldlevel(wp, lnum)
6379 win_T *wp;
6380 long lnum;
6381{
6382 int level = 0;
6383 int i;
6384
6385 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006386 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006387 {
6388 syntax_start(wp, lnum);
6389
6390 for (i = 0; i < current_state.ga_len; ++i)
6391 if (CUR_STATE(i).si_flags & HL_FOLD)
6392 ++level;
6393 }
6394 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006395 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006396 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006397 if (level < 0)
6398 level = 0;
6399 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006400 return level;
6401}
6402#endif
6403
6404#endif /* FEAT_SYN_HL */
6405
6406
6407/**************************************
6408 * Highlighting stuff *
6409 **************************************/
6410
6411/*
6412 * The default highlight groups. These are compiled-in for fast startup and
6413 * they still work when the runtime files can't be found.
6414 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006415 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6416 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006417 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006418#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006419# define CENT(a, b) b
6420#else
6421# define CENT(a, b) a
6422#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006423static char *(highlight_init_both[]) =
6424 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006425 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6426 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6427 CENT("IncSearch term=reverse cterm=reverse",
6428 "IncSearch term=reverse cterm=reverse gui=reverse"),
6429 CENT("ModeMsg term=bold cterm=bold",
6430 "ModeMsg term=bold cterm=bold gui=bold"),
6431 CENT("NonText term=bold ctermfg=Blue",
6432 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6433 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6434 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6435 CENT("StatusLineNC term=reverse cterm=reverse",
6436 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006437#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006438 CENT("VertSplit term=reverse cterm=reverse",
6439 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006440#endif
6441#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006442 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6443 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006444#endif
6445#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006446 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6447 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006448#endif
6449#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006450 CENT("PmenuThumb cterm=reverse",
6451 "PmenuThumb cterm=reverse gui=reverse"),
6452 CENT("PmenuSbar ctermbg=Grey",
6453 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006454#endif
6455#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006456 CENT("TabLineSel term=bold cterm=bold",
6457 "TabLineSel term=bold cterm=bold gui=bold"),
6458 CENT("TabLineFill term=reverse cterm=reverse",
6459 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006460#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006461#ifdef FEAT_GUI
6462 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006463 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006464#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006465 NULL
6466 };
6467
6468static char *(highlight_init_light[]) =
6469 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006470 CENT("Directory term=bold ctermfg=DarkBlue",
6471 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6472 CENT("LineNr term=underline ctermfg=Brown",
6473 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6474 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6475 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6476 CENT("Question term=standout ctermfg=DarkGreen",
6477 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6478 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6479 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006480#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006481 CENT("SpellBad term=reverse ctermbg=LightRed",
6482 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6483 CENT("SpellCap term=reverse ctermbg=LightBlue",
6484 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6485 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6486 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6487 CENT("SpellLocal term=underline ctermbg=Cyan",
6488 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006489#endif
6490#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006491 CENT("Pmenu ctermbg=LightMagenta",
6492 "Pmenu ctermbg=LightMagenta guibg=LightMagenta"),
6493 CENT("PmenuSel ctermbg=LightGrey",
6494 "PmenuSel ctermbg=LightGrey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006495#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006496 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6497 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6498 CENT("Title term=bold ctermfg=DarkMagenta",
6499 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6500 CENT("WarningMsg term=standout ctermfg=DarkRed",
6501 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006502#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006503 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6504 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006505#endif
6506#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006507 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6508 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6509 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6510 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006511#endif
6512#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006513 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6514 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006515#endif
6516#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006517 CENT("Visual term=reverse",
6518 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006519#endif
6520#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006521 CENT("DiffAdd term=bold ctermbg=LightBlue",
6522 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6523 CENT("DiffChange term=bold ctermbg=LightMagenta",
6524 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6525 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6526 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006527#endif
6528#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006529 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6530 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006531#endif
6532#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006533 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006534 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006535 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006536 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006537 CENT("ColorColumn term=reverse ctermbg=LightRed",
6538 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006539#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006540#ifdef FEAT_CONCEAL
6541 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6542 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6543#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006544#ifdef FEAT_AUTOCMD
6545 CENT("MatchParen term=reverse ctermbg=Cyan",
6546 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6547#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006548#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006549 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006550#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006551 NULL
6552 };
6553
6554static char *(highlight_init_dark[]) =
6555 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006556 CENT("Directory term=bold ctermfg=LightCyan",
6557 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6558 CENT("LineNr term=underline ctermfg=Yellow",
6559 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6560 CENT("MoreMsg term=bold ctermfg=LightGreen",
6561 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6562 CENT("Question term=standout ctermfg=LightGreen",
6563 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6564 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6565 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6566 CENT("SpecialKey term=bold ctermfg=LightBlue",
6567 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006568#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006569 CENT("SpellBad term=reverse ctermbg=Red",
6570 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6571 CENT("SpellCap term=reverse ctermbg=Blue",
6572 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6573 CENT("SpellRare term=reverse ctermbg=Magenta",
6574 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6575 CENT("SpellLocal term=underline ctermbg=Cyan",
6576 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006577#endif
6578#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006579 CENT("Pmenu ctermbg=Magenta",
6580 "Pmenu ctermbg=Magenta guibg=Magenta"),
6581 CENT("PmenuSel ctermbg=DarkGrey",
6582 "PmenuSel ctermbg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006583#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006584 CENT("Title term=bold ctermfg=LightMagenta",
6585 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6586 CENT("WarningMsg term=standout ctermfg=LightRed",
6587 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006588#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006589 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6590 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006591#endif
6592#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006593 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6594 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6595 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6596 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006597#endif
6598#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006599 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6600 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006601#endif
6602#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006603 CENT("Visual term=reverse",
6604 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006605#endif
6606#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006607 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6608 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6609 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6610 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6611 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6612 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006613#endif
6614#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006615 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6616 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006617#endif
6618#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006619 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006620 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006621 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006622 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006623 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6624 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006625#endif
6626#ifdef FEAT_AUTOCMD
6627 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6628 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006629#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006630#ifdef FEAT_CONCEAL
6631 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6632 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6633#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006634#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006635 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006636#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006637 NULL
6638 };
6639
6640 void
6641init_highlight(both, reset)
6642 int both; /* include groups where 'bg' doesn't matter */
6643 int reset; /* clear group first */
6644{
6645 int i;
6646 char **pp;
6647 static int had_both = FALSE;
6648#ifdef FEAT_EVAL
6649 char_u *p;
6650
6651 /*
6652 * Try finding the color scheme file. Used when a color file was loaded
6653 * and 'background' or 't_Co' is changed.
6654 */
6655 p = get_var_value((char_u *)"g:colors_name");
6656 if (p != NULL && load_colors(p) == OK)
6657 return;
6658#endif
6659
6660 /*
6661 * Didn't use a color file, use the compiled-in colors.
6662 */
6663 if (both)
6664 {
6665 had_both = TRUE;
6666 pp = highlight_init_both;
6667 for (i = 0; pp[i] != NULL; ++i)
6668 do_highlight((char_u *)pp[i], reset, TRUE);
6669 }
6670 else if (!had_both)
6671 /* Don't do anything before the call with both == TRUE from main().
6672 * Not everything has been setup then, and that call will overrule
6673 * everything anyway. */
6674 return;
6675
6676 if (*p_bg == 'l')
6677 pp = highlight_init_light;
6678 else
6679 pp = highlight_init_dark;
6680 for (i = 0; pp[i] != NULL; ++i)
6681 do_highlight((char_u *)pp[i], reset, TRUE);
6682
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006683 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006684 * depend on the number of colors available.
6685 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00006686 * to avoid Statement highlighted text disappears.
6687 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00006688 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00006689 do_highlight((char_u *)(*p_bg == 'l'
6690 ? "Visual cterm=NONE ctermbg=LightGrey"
6691 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006692 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006693 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00006694 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
6695 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006696 if (*p_bg == 'l')
6697 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
6698 }
Bram Moolenaarab194812005-09-14 21:40:12 +00006699
Bram Moolenaar071d4272004-06-13 20:20:40 +00006700#ifdef FEAT_SYN_HL
6701 /*
6702 * If syntax highlighting is enabled load the highlighting for it.
6703 */
6704 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006705 {
6706 static int recursive = 0;
6707
6708 if (recursive >= 5)
6709 EMSG(_("E679: recursive loop loading syncolor.vim"));
6710 else
6711 {
6712 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006713 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006714 --recursive;
6715 }
6716 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006717#endif
6718}
6719
6720/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006721 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006722 * Return OK for success, FAIL for failure.
6723 */
6724 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006725load_colors(name)
6726 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006727{
6728 char_u *buf;
6729 int retval = FAIL;
6730 static int recursive = FALSE;
6731
6732 /* When being called recursively, this is probably because setting
6733 * 'background' caused the highlighting to be reloaded. This means it is
6734 * working, thus we should return OK. */
6735 if (recursive)
6736 return OK;
6737
6738 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006739 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006740 if (buf != NULL)
6741 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006742 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006743 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006744 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006745#ifdef FEAT_AUTOCMD
6746 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6747#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006748 }
6749 recursive = FALSE;
6750
6751 return retval;
6752}
6753
6754/*
6755 * Handle the ":highlight .." command.
6756 * When using ":hi clear" this is called recursively for each group with
6757 * "forceit" and "init" both TRUE.
6758 */
6759 void
6760do_highlight(line, forceit, init)
6761 char_u *line;
6762 int forceit;
6763 int init; /* TRUE when called for initializing */
6764{
6765 char_u *name_end;
6766 char_u *p;
6767 char_u *linep;
6768 char_u *key_start;
6769 char_u *arg_start;
6770 char_u *key = NULL, *arg = NULL;
6771 long i;
6772 int off;
6773 int len;
6774 int attr;
6775 int id;
6776 int idx;
6777 int dodefault = FALSE;
6778 int doclear = FALSE;
6779 int dolink = FALSE;
6780 int error = FALSE;
6781 int color;
6782 int is_normal_group = FALSE; /* "Normal" group */
6783#ifdef FEAT_GUI_X11
6784 int is_menu_group = FALSE; /* "Menu" group */
6785 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6786 int is_tooltip_group = FALSE; /* "Tooltip" group */
6787 int do_colors = FALSE; /* need to update colors? */
6788#else
6789# define is_menu_group 0
6790# define is_tooltip_group 0
6791#endif
6792
6793 /*
6794 * If no argument, list current highlighting.
6795 */
6796 if (ends_excmd(*line))
6797 {
6798 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6799 /* TODO: only call when the group has attributes set */
6800 highlight_list_one((int)i);
6801 return;
6802 }
6803
6804 /*
6805 * Isolate the name.
6806 */
6807 name_end = skiptowhite(line);
6808 linep = skipwhite(name_end);
6809
6810 /*
6811 * Check for "default" argument.
6812 */
6813 if (STRNCMP(line, "default", name_end - line) == 0)
6814 {
6815 dodefault = TRUE;
6816 line = linep;
6817 name_end = skiptowhite(line);
6818 linep = skipwhite(name_end);
6819 }
6820
6821 /*
6822 * Check for "clear" or "link" argument.
6823 */
6824 if (STRNCMP(line, "clear", name_end - line) == 0)
6825 doclear = TRUE;
6826 if (STRNCMP(line, "link", name_end - line) == 0)
6827 dolink = TRUE;
6828
6829 /*
6830 * ":highlight {group-name}": list highlighting for one group.
6831 */
6832 if (!doclear && !dolink && ends_excmd(*linep))
6833 {
6834 id = syn_namen2id(line, (int)(name_end - line));
6835 if (id == 0)
6836 EMSG2(_("E411: highlight group not found: %s"), line);
6837 else
6838 highlight_list_one(id);
6839 return;
6840 }
6841
6842 /*
6843 * Handle ":highlight link {from} {to}" command.
6844 */
6845 if (dolink)
6846 {
6847 char_u *from_start = linep;
6848 char_u *from_end;
6849 char_u *to_start;
6850 char_u *to_end;
6851 int from_id;
6852 int to_id;
6853
6854 from_end = skiptowhite(from_start);
6855 to_start = skipwhite(from_end);
6856 to_end = skiptowhite(to_start);
6857
6858 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6859 {
6860 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6861 from_start);
6862 return;
6863 }
6864
6865 if (!ends_excmd(*skipwhite(to_end)))
6866 {
6867 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6868 return;
6869 }
6870
6871 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6872 if (STRNCMP(to_start, "NONE", 4) == 0)
6873 to_id = 0;
6874 else
6875 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6876
6877 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6878 {
6879 /*
6880 * Don't allow a link when there already is some highlighting
6881 * for the group, unless '!' is used
6882 */
6883 if (to_id > 0 && !forceit && !init
6884 && hl_has_settings(from_id - 1, dodefault))
6885 {
6886 if (sourcing_name == NULL && !dodefault)
6887 EMSG(_("E414: group has settings, highlight link ignored"));
6888 }
6889 else
6890 {
6891 if (!init)
6892 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6893 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00006894#ifdef FEAT_EVAL
6895 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6896#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00006897 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006898 }
6899 }
6900
6901 /* Only call highlight_changed() once, after sourcing a syntax file */
6902 need_highlight_changed = TRUE;
6903
6904 return;
6905 }
6906
6907 if (doclear)
6908 {
6909 /*
6910 * ":highlight clear [group]" command.
6911 */
6912 line = linep;
6913 if (ends_excmd(*line))
6914 {
6915#ifdef FEAT_GUI
6916 /* First, we do not destroy the old values, but allocate the new
6917 * ones and update the display. THEN we destroy the old values.
6918 * If we destroy the old values first, then the old values
6919 * (such as GuiFont's or GuiFontset's) will still be displayed but
6920 * invalid because they were free'd.
6921 */
6922 if (gui.in_use)
6923 {
6924# ifdef FEAT_BEVAL_TIP
6925 gui_init_tooltip_font();
6926# endif
6927# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6928 gui_init_menu_font();
6929# endif
6930 }
6931# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6932 gui_mch_def_colors();
6933# endif
6934# ifdef FEAT_GUI_X11
6935# ifdef FEAT_MENU
6936
6937 /* This only needs to be done when there is no Menu highlight
6938 * group defined by default, which IS currently the case.
6939 */
6940 gui_mch_new_menu_colors();
6941# endif
6942 if (gui.in_use)
6943 {
6944 gui_new_scrollbar_colors();
6945# ifdef FEAT_BEVAL
6946 gui_mch_new_tooltip_colors();
6947# endif
6948# ifdef FEAT_MENU
6949 gui_mch_new_menu_font();
6950# endif
6951 }
6952# endif
6953
6954 /* Ok, we're done allocating the new default graphics items.
6955 * The screen should already be refreshed at this point.
6956 * It is now Ok to clear out the old data.
6957 */
6958#endif
6959#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00006960 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006961#endif
6962 restore_cterm_colors();
6963
6964 /*
6965 * Clear all default highlight groups and load the defaults.
6966 */
6967 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6968 highlight_clear(idx);
6969 init_highlight(TRUE, TRUE);
6970#ifdef FEAT_GUI
6971 if (gui.in_use)
6972 highlight_gui_started();
6973#endif
6974 highlight_changed();
6975 redraw_later_clear();
6976 return;
6977 }
6978 name_end = skiptowhite(line);
6979 linep = skipwhite(name_end);
6980 }
6981
6982 /*
6983 * Find the group name in the table. If it does not exist yet, add it.
6984 */
6985 id = syn_check_group(line, (int)(name_end - line));
6986 if (id == 0) /* failed (out of memory) */
6987 return;
6988 idx = id - 1; /* index is ID minus one */
6989
6990 /* Return if "default" was used and the group already has settings. */
6991 if (dodefault && hl_has_settings(idx, TRUE))
6992 return;
6993
6994 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
6995 is_normal_group = TRUE;
6996#ifdef FEAT_GUI_X11
6997 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
6998 is_menu_group = TRUE;
6999 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7000 is_scrollbar_group = TRUE;
7001 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7002 is_tooltip_group = TRUE;
7003#endif
7004
7005 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7006 if (doclear || (forceit && init))
7007 {
7008 highlight_clear(idx);
7009 if (!doclear)
7010 HL_TABLE()[idx].sg_set = 0;
7011 }
7012
7013 if (!doclear)
7014 while (!ends_excmd(*linep))
7015 {
7016 key_start = linep;
7017 if (*linep == '=')
7018 {
7019 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7020 error = TRUE;
7021 break;
7022 }
7023
7024 /*
7025 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7026 * "guibg").
7027 */
7028 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7029 ++linep;
7030 vim_free(key);
7031 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7032 if (key == NULL)
7033 {
7034 error = TRUE;
7035 break;
7036 }
7037 linep = skipwhite(linep);
7038
7039 if (STRCMP(key, "NONE") == 0)
7040 {
7041 if (!init || HL_TABLE()[idx].sg_set == 0)
7042 {
7043 if (!init)
7044 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7045 highlight_clear(idx);
7046 }
7047 continue;
7048 }
7049
7050 /*
7051 * Check for the equal sign.
7052 */
7053 if (*linep != '=')
7054 {
7055 EMSG2(_("E416: missing equal sign: %s"), key_start);
7056 error = TRUE;
7057 break;
7058 }
7059 ++linep;
7060
7061 /*
7062 * Isolate the argument.
7063 */
7064 linep = skipwhite(linep);
7065 if (*linep == '\'') /* guifg='color name' */
7066 {
7067 arg_start = ++linep;
7068 linep = vim_strchr(linep, '\'');
7069 if (linep == NULL)
7070 {
7071 EMSG2(_(e_invarg2), key_start);
7072 error = TRUE;
7073 break;
7074 }
7075 }
7076 else
7077 {
7078 arg_start = linep;
7079 linep = skiptowhite(linep);
7080 }
7081 if (linep == arg_start)
7082 {
7083 EMSG2(_("E417: missing argument: %s"), key_start);
7084 error = TRUE;
7085 break;
7086 }
7087 vim_free(arg);
7088 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7089 if (arg == NULL)
7090 {
7091 error = TRUE;
7092 break;
7093 }
7094 if (*linep == '\'')
7095 ++linep;
7096
7097 /*
7098 * Store the argument.
7099 */
7100 if ( STRCMP(key, "TERM") == 0
7101 || STRCMP(key, "CTERM") == 0
7102 || STRCMP(key, "GUI") == 0)
7103 {
7104 attr = 0;
7105 off = 0;
7106 while (arg[off] != NUL)
7107 {
7108 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7109 {
7110 len = (int)STRLEN(hl_name_table[i]);
7111 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7112 {
7113 attr |= hl_attr_table[i];
7114 off += len;
7115 break;
7116 }
7117 }
7118 if (i < 0)
7119 {
7120 EMSG2(_("E418: Illegal value: %s"), arg);
7121 error = TRUE;
7122 break;
7123 }
7124 if (arg[off] == ',') /* another one follows */
7125 ++off;
7126 }
7127 if (error)
7128 break;
7129 if (*key == 'T')
7130 {
7131 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7132 {
7133 if (!init)
7134 HL_TABLE()[idx].sg_set |= SG_TERM;
7135 HL_TABLE()[idx].sg_term = attr;
7136 }
7137 }
7138 else if (*key == 'C')
7139 {
7140 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7141 {
7142 if (!init)
7143 HL_TABLE()[idx].sg_set |= SG_CTERM;
7144 HL_TABLE()[idx].sg_cterm = attr;
7145 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7146 }
7147 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007148#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007149 else
7150 {
7151 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7152 {
7153 if (!init)
7154 HL_TABLE()[idx].sg_set |= SG_GUI;
7155 HL_TABLE()[idx].sg_gui = attr;
7156 }
7157 }
7158#endif
7159 }
7160 else if (STRCMP(key, "FONT") == 0)
7161 {
7162 /* in non-GUI fonts are simply ignored */
7163#ifdef FEAT_GUI
7164 if (!gui.shell_created)
7165 {
7166 /* GUI not started yet, always accept the name. */
7167 vim_free(HL_TABLE()[idx].sg_font_name);
7168 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7169 }
7170 else
7171 {
7172 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7173# ifdef FEAT_XFONTSET
7174 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7175# endif
7176 /* First, save the current font/fontset.
7177 * Then try to allocate the font/fontset.
7178 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7179 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7180 */
7181
7182 HL_TABLE()[idx].sg_font = NOFONT;
7183# ifdef FEAT_XFONTSET
7184 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7185# endif
7186 hl_do_font(idx, arg, is_normal_group, is_menu_group,
7187 is_tooltip_group);
7188
7189# ifdef FEAT_XFONTSET
7190 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7191 {
7192 /* New fontset was accepted. Free the old one, if there was
7193 * one.
7194 */
7195 gui_mch_free_fontset(temp_sg_fontset);
7196 vim_free(HL_TABLE()[idx].sg_font_name);
7197 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7198 }
7199 else
7200 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7201# endif
7202 if (HL_TABLE()[idx].sg_font != NOFONT)
7203 {
7204 /* New font was accepted. Free the old one, if there was
7205 * one.
7206 */
7207 gui_mch_free_font(temp_sg_font);
7208 vim_free(HL_TABLE()[idx].sg_font_name);
7209 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7210 }
7211 else
7212 HL_TABLE()[idx].sg_font = temp_sg_font;
7213 }
7214#endif
7215 }
7216 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7217 {
7218 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7219 {
7220 if (!init)
7221 HL_TABLE()[idx].sg_set |= SG_CTERM;
7222
7223 /* When setting the foreground color, and previously the "bold"
7224 * flag was set for a light color, reset it now */
7225 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7226 {
7227 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7228 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7229 }
7230
7231 if (VIM_ISDIGIT(*arg))
7232 color = atoi((char *)arg);
7233 else if (STRICMP(arg, "fg") == 0)
7234 {
7235 if (cterm_normal_fg_color)
7236 color = cterm_normal_fg_color - 1;
7237 else
7238 {
7239 EMSG(_("E419: FG color unknown"));
7240 error = TRUE;
7241 break;
7242 }
7243 }
7244 else if (STRICMP(arg, "bg") == 0)
7245 {
7246 if (cterm_normal_bg_color > 0)
7247 color = cterm_normal_bg_color - 1;
7248 else
7249 {
7250 EMSG(_("E420: BG color unknown"));
7251 error = TRUE;
7252 break;
7253 }
7254 }
7255 else
7256 {
7257 static char *(color_names[28]) = {
7258 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7259 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7260 "Gray", "Grey",
7261 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7262 "Blue", "LightBlue", "Green", "LightGreen",
7263 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7264 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7265 static int color_numbers_16[28] = {0, 1, 2, 3,
7266 4, 5, 6, 6,
7267 7, 7,
7268 7, 7, 8, 8,
7269 9, 9, 10, 10,
7270 11, 11, 12, 12, 13,
7271 13, 14, 14, 15, -1};
7272 /* for xterm with 88 colors... */
7273 static int color_numbers_88[28] = {0, 4, 2, 6,
7274 1, 5, 32, 72,
7275 84, 84,
7276 7, 7, 82, 82,
7277 12, 43, 10, 61,
7278 14, 63, 9, 74, 13,
7279 75, 11, 78, 15, -1};
7280 /* for xterm with 256 colors... */
7281 static int color_numbers_256[28] = {0, 4, 2, 6,
7282 1, 5, 130, 130,
7283 248, 248,
7284 7, 7, 242, 242,
7285 12, 81, 10, 121,
7286 14, 159, 9, 224, 13,
7287 225, 11, 229, 15, -1};
7288 /* for terminals with less than 16 colors... */
7289 static int color_numbers_8[28] = {0, 4, 2, 6,
7290 1, 5, 3, 3,
7291 7, 7,
7292 7, 7, 0+8, 0+8,
7293 4+8, 4+8, 2+8, 2+8,
7294 6+8, 6+8, 1+8, 1+8, 5+8,
7295 5+8, 3+8, 3+8, 7+8, -1};
7296#if defined(__QNXNTO__)
7297 static int *color_numbers_8_qansi = color_numbers_8;
7298 /* On qnx, the 8 & 16 color arrays are the same */
7299 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7300 color_numbers_8_qansi = color_numbers_16;
7301#endif
7302
7303 /* reduce calls to STRICMP a bit, it can be slow */
7304 off = TOUPPER_ASC(*arg);
7305 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7306 if (off == color_names[i][0]
7307 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7308 break;
7309 if (i < 0)
7310 {
7311 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7312 error = TRUE;
7313 break;
7314 }
7315
7316 /* Use the _16 table to check if its a valid color name. */
7317 color = color_numbers_16[i];
7318 if (color >= 0)
7319 {
7320 if (t_colors == 8)
7321 {
7322 /* t_Co is 8: use the 8 colors table */
7323#if defined(__QNXNTO__)
7324 color = color_numbers_8_qansi[i];
7325#else
7326 color = color_numbers_8[i];
7327#endif
7328 if (key[5] == 'F')
7329 {
7330 /* set/reset bold attribute to get light foreground
7331 * colors (on some terminals, e.g. "linux") */
7332 if (color & 8)
7333 {
7334 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7335 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7336 }
7337 else
7338 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7339 }
7340 color &= 7; /* truncate to 8 colors */
7341 }
7342 else if (t_colors == 16 || t_colors == 88
7343 || t_colors == 256)
7344 {
7345 /*
7346 * Guess: if the termcap entry ends in 'm', it is
7347 * probably an xterm-like terminal. Use the changed
7348 * order for colors.
7349 */
7350 if (*T_CAF != NUL)
7351 p = T_CAF;
7352 else
7353 p = T_CSF;
7354 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7355 switch (t_colors)
7356 {
7357 case 16:
7358 color = color_numbers_8[i];
7359 break;
7360 case 88:
7361 color = color_numbers_88[i];
7362 break;
7363 case 256:
7364 color = color_numbers_256[i];
7365 break;
7366 }
7367 }
7368 }
7369 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007370 /* Add one to the argument, to avoid zero. Zero is used for
7371 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007372 if (key[5] == 'F')
7373 {
7374 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7375 if (is_normal_group)
7376 {
7377 cterm_normal_fg_color = color + 1;
7378 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7379#ifdef FEAT_GUI
7380 /* Don't do this if the GUI is used. */
7381 if (!gui.in_use && !gui.starting)
7382#endif
7383 {
7384 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007385 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007386 term_fg_color(color);
7387 }
7388 }
7389 }
7390 else
7391 {
7392 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7393 if (is_normal_group)
7394 {
7395 cterm_normal_bg_color = color + 1;
7396#ifdef FEAT_GUI
7397 /* Don't mess with 'background' if the GUI is used. */
7398 if (!gui.in_use && !gui.starting)
7399#endif
7400 {
7401 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007402 if (color >= 0)
7403 {
7404 if (termcap_active)
7405 term_bg_color(color);
7406 if (t_colors < 16)
7407 i = (color == 0 || color == 4);
7408 else
7409 i = (color < 7 || color == 8);
7410 /* Set the 'background' option if the value is
7411 * wrong. */
7412 if (i != (*p_bg == 'd'))
7413 set_option_value((char_u *)"bg", 0L,
7414 i ? (char_u *)"dark"
7415 : (char_u *)"light", 0);
7416 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007417 }
7418 }
7419 }
7420 }
7421 }
7422 else if (STRCMP(key, "GUIFG") == 0)
7423 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007424#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007425 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007426 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007427 if (!init)
7428 HL_TABLE()[idx].sg_set |= SG_GUI;
7429
Bram Moolenaar61623362010-07-14 22:04:22 +02007430# ifdef FEAT_GUI
7431 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007432 i = color_name2handle(arg);
7433 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7434 {
7435 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007436# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007437 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7438 if (STRCMP(arg, "NONE"))
7439 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7440 else
7441 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007442# ifdef FEAT_GUI
7443# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007444 if (is_menu_group)
7445 gui.menu_fg_pixel = i;
7446 if (is_scrollbar_group)
7447 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007448# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007449 if (is_tooltip_group)
7450 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007451# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007452 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007453# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007454 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007455# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007456 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007457#endif
7458 }
7459 else if (STRCMP(key, "GUIBG") == 0)
7460 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007461#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007462 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007463 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007464 if (!init)
7465 HL_TABLE()[idx].sg_set |= SG_GUI;
7466
Bram Moolenaar61623362010-07-14 22:04:22 +02007467# ifdef FEAT_GUI
7468 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007469 i = color_name2handle(arg);
7470 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7471 {
7472 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007473# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007474 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7475 if (STRCMP(arg, "NONE") != 0)
7476 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7477 else
7478 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007479# ifdef FEAT_GUI
7480# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007481 if (is_menu_group)
7482 gui.menu_bg_pixel = i;
7483 if (is_scrollbar_group)
7484 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007485# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007486 if (is_tooltip_group)
7487 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007488# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007489 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007490# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007491 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007492# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007493 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007494#endif
7495 }
7496 else if (STRCMP(key, "GUISP") == 0)
7497 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007498#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007499 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7500 {
7501 if (!init)
7502 HL_TABLE()[idx].sg_set |= SG_GUI;
7503
Bram Moolenaar61623362010-07-14 22:04:22 +02007504# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007505 i = color_name2handle(arg);
7506 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7507 {
7508 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007509# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007510 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7511 if (STRCMP(arg, "NONE") != 0)
7512 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7513 else
7514 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007515# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007516 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007517# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007518 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007519#endif
7520 }
7521 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7522 {
7523 char_u buf[100];
7524 char_u *tname;
7525
7526 if (!init)
7527 HL_TABLE()[idx].sg_set |= SG_TERM;
7528
7529 /*
7530 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007531 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007532 */
7533 if (STRNCMP(arg, "t_", 2) == 0)
7534 {
7535 off = 0;
7536 buf[0] = 0;
7537 while (arg[off] != NUL)
7538 {
7539 /* Isolate one termcap name */
7540 for (len = 0; arg[off + len] &&
7541 arg[off + len] != ','; ++len)
7542 ;
7543 tname = vim_strnsave(arg + off, len);
7544 if (tname == NULL) /* out of memory */
7545 {
7546 error = TRUE;
7547 break;
7548 }
7549 /* lookup the escape sequence for the item */
7550 p = get_term_code(tname);
7551 vim_free(tname);
7552 if (p == NULL) /* ignore non-existing things */
7553 p = (char_u *)"";
7554
7555 /* Append it to the already found stuff */
7556 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7557 {
7558 EMSG2(_("E422: terminal code too long: %s"), arg);
7559 error = TRUE;
7560 break;
7561 }
7562 STRCAT(buf, p);
7563
7564 /* Advance to the next item */
7565 off += len;
7566 if (arg[off] == ',') /* another one follows */
7567 ++off;
7568 }
7569 }
7570 else
7571 {
7572 /*
7573 * Copy characters from arg[] to buf[], translating <> codes.
7574 */
7575 for (p = arg, off = 0; off < 100 && *p; )
7576 {
7577 len = trans_special(&p, buf + off, FALSE);
7578 if (len) /* recognized special char */
7579 off += len;
7580 else /* copy as normal char */
7581 buf[off++] = *p++;
7582 }
7583 buf[off] = NUL;
7584 }
7585 if (error)
7586 break;
7587
7588 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7589 p = NULL;
7590 else
7591 p = vim_strsave(buf);
7592 if (key[2] == 'A')
7593 {
7594 vim_free(HL_TABLE()[idx].sg_start);
7595 HL_TABLE()[idx].sg_start = p;
7596 }
7597 else
7598 {
7599 vim_free(HL_TABLE()[idx].sg_stop);
7600 HL_TABLE()[idx].sg_stop = p;
7601 }
7602 }
7603 else
7604 {
7605 EMSG2(_("E423: Illegal argument: %s"), key_start);
7606 error = TRUE;
7607 break;
7608 }
7609
7610 /*
7611 * When highlighting has been given for a group, don't link it.
7612 */
7613 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7614 HL_TABLE()[idx].sg_link = 0;
7615
7616 /*
7617 * Continue with next argument.
7618 */
7619 linep = skipwhite(linep);
7620 }
7621
7622 /*
7623 * If there is an error, and it's a new entry, remove it from the table.
7624 */
7625 if (error && idx == highlight_ga.ga_len)
7626 syn_unadd_group();
7627 else
7628 {
7629 if (is_normal_group)
7630 {
7631 HL_TABLE()[idx].sg_term_attr = 0;
7632 HL_TABLE()[idx].sg_cterm_attr = 0;
7633#ifdef FEAT_GUI
7634 HL_TABLE()[idx].sg_gui_attr = 0;
7635 /*
7636 * Need to update all groups, because they might be using "bg"
7637 * and/or "fg", which have been changed now.
7638 */
7639 if (gui.in_use)
7640 highlight_gui_started();
7641#endif
7642 }
7643#ifdef FEAT_GUI_X11
7644# ifdef FEAT_MENU
7645 else if (is_menu_group)
7646 {
7647 if (gui.in_use && do_colors)
7648 gui_mch_new_menu_colors();
7649 }
7650# endif
7651 else if (is_scrollbar_group)
7652 {
7653 if (gui.in_use && do_colors)
7654 gui_new_scrollbar_colors();
7655 }
7656# ifdef FEAT_BEVAL
7657 else if (is_tooltip_group)
7658 {
7659 if (gui.in_use && do_colors)
7660 gui_mch_new_tooltip_colors();
7661 }
7662# endif
7663#endif
7664 else
7665 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007666#ifdef FEAT_EVAL
7667 HL_TABLE()[idx].sg_scriptID = current_SID;
7668#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007669 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007670 }
7671 vim_free(key);
7672 vim_free(arg);
7673
7674 /* Only call highlight_changed() once, after sourcing a syntax file */
7675 need_highlight_changed = TRUE;
7676}
7677
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007678#if defined(EXITFREE) || defined(PROTO)
7679 void
7680free_highlight()
7681{
7682 int i;
7683
7684 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007685 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007686 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007687 vim_free(HL_TABLE()[i].sg_name);
7688 vim_free(HL_TABLE()[i].sg_name_u);
7689 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007690 ga_clear(&highlight_ga);
7691}
7692#endif
7693
Bram Moolenaar071d4272004-06-13 20:20:40 +00007694/*
7695 * Reset the cterm colors to what they were before Vim was started, if
7696 * possible. Otherwise reset them to zero.
7697 */
7698 void
7699restore_cterm_colors()
7700{
7701#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7702 /* Since t_me has been set, this probably means that the user
7703 * wants to use this as default colors. Need to reset default
7704 * background/foreground colors. */
7705 mch_set_normal_colors();
7706#else
7707 cterm_normal_fg_color = 0;
7708 cterm_normal_fg_bold = 0;
7709 cterm_normal_bg_color = 0;
7710#endif
7711}
7712
7713/*
7714 * Return TRUE if highlight group "idx" has any settings.
7715 * When "check_link" is TRUE also check for an existing link.
7716 */
7717 static int
7718hl_has_settings(idx, check_link)
7719 int idx;
7720 int check_link;
7721{
7722 return ( HL_TABLE()[idx].sg_term_attr != 0
7723 || HL_TABLE()[idx].sg_cterm_attr != 0
7724#ifdef FEAT_GUI
7725 || HL_TABLE()[idx].sg_gui_attr != 0
7726#endif
7727 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7728}
7729
7730/*
7731 * Clear highlighting for one group.
7732 */
7733 static void
7734highlight_clear(idx)
7735 int idx;
7736{
7737 HL_TABLE()[idx].sg_term = 0;
7738 vim_free(HL_TABLE()[idx].sg_start);
7739 HL_TABLE()[idx].sg_start = NULL;
7740 vim_free(HL_TABLE()[idx].sg_stop);
7741 HL_TABLE()[idx].sg_stop = NULL;
7742 HL_TABLE()[idx].sg_term_attr = 0;
7743 HL_TABLE()[idx].sg_cterm = 0;
7744 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7745 HL_TABLE()[idx].sg_cterm_fg = 0;
7746 HL_TABLE()[idx].sg_cterm_bg = 0;
7747 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02007748#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007749 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007750 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7751 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007752 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7753 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007754 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7755 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007756#endif
7757#ifdef FEAT_GUI
7758 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7759 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7760 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007761 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7762 HL_TABLE()[idx].sg_font = NOFONT;
7763# ifdef FEAT_XFONTSET
7764 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7765 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7766# endif
7767 vim_free(HL_TABLE()[idx].sg_font_name);
7768 HL_TABLE()[idx].sg_font_name = NULL;
7769 HL_TABLE()[idx].sg_gui_attr = 0;
7770#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00007771#ifdef FEAT_EVAL
7772 /* Clear the script ID only when there is no link, since that is not
7773 * cleared. */
7774 if (HL_TABLE()[idx].sg_link == 0)
7775 HL_TABLE()[idx].sg_scriptID = 0;
7776#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007777}
7778
7779#if defined(FEAT_GUI) || defined(PROTO)
7780/*
7781 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00007782 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00007783 * "Tooltip" colors.
7784 */
7785 void
7786set_normal_colors()
7787{
7788 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007789 &gui.norm_pixel, &gui.back_pixel,
7790 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007791 {
7792 gui_mch_new_colors();
7793 must_redraw = CLEAR;
7794 }
7795#ifdef FEAT_GUI_X11
7796 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007797 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7798 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007799 {
7800# ifdef FEAT_MENU
7801 gui_mch_new_menu_colors();
7802# endif
7803 must_redraw = CLEAR;
7804 }
7805# ifdef FEAT_BEVAL
7806 if (set_group_colors((char_u *)"Tooltip",
7807 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7808 FALSE, FALSE, TRUE))
7809 {
7810# ifdef FEAT_TOOLBAR
7811 gui_mch_new_tooltip_colors();
7812# endif
7813 must_redraw = CLEAR;
7814 }
7815#endif
7816 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007817 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7818 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007819 {
7820 gui_new_scrollbar_colors();
7821 must_redraw = CLEAR;
7822 }
7823#endif
7824}
7825
7826/*
7827 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7828 */
7829 static int
7830set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7831 char_u *name;
7832 guicolor_T *fgp;
7833 guicolor_T *bgp;
7834 int do_menu;
7835 int use_norm;
7836 int do_tooltip;
7837{
7838 int idx;
7839
7840 idx = syn_name2id(name) - 1;
7841 if (idx >= 0)
7842 {
7843 gui_do_one_color(idx, do_menu, do_tooltip);
7844
7845 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7846 *fgp = HL_TABLE()[idx].sg_gui_fg;
7847 else if (use_norm)
7848 *fgp = gui.def_norm_pixel;
7849 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7850 *bgp = HL_TABLE()[idx].sg_gui_bg;
7851 else if (use_norm)
7852 *bgp = gui.def_back_pixel;
7853 return TRUE;
7854 }
7855 return FALSE;
7856}
7857
7858/*
7859 * Get the font of the "Normal" group.
7860 * Returns "" when it's not found or not set.
7861 */
7862 char_u *
7863hl_get_font_name()
7864{
7865 int id;
7866 char_u *s;
7867
7868 id = syn_name2id((char_u *)"Normal");
7869 if (id > 0)
7870 {
7871 s = HL_TABLE()[id - 1].sg_font_name;
7872 if (s != NULL)
7873 return s;
7874 }
7875 return (char_u *)"";
7876}
7877
7878/*
7879 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7880 * actually chosen to be used.
7881 */
7882 void
7883hl_set_font_name(font_name)
7884 char_u *font_name;
7885{
7886 int id;
7887
7888 id = syn_name2id((char_u *)"Normal");
7889 if (id > 0)
7890 {
7891 vim_free(HL_TABLE()[id - 1].sg_font_name);
7892 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7893 }
7894}
7895
7896/*
7897 * Set background color for "Normal" group. Called by gui_set_bg_color()
7898 * when the color is known.
7899 */
7900 void
7901hl_set_bg_color_name(name)
7902 char_u *name; /* must have been allocated */
7903{
7904 int id;
7905
7906 if (name != NULL)
7907 {
7908 id = syn_name2id((char_u *)"Normal");
7909 if (id > 0)
7910 {
7911 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7912 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7913 }
7914 }
7915}
7916
7917/*
7918 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7919 * when the color is known.
7920 */
7921 void
7922hl_set_fg_color_name(name)
7923 char_u *name; /* must have been allocated */
7924{
7925 int id;
7926
7927 if (name != NULL)
7928 {
7929 id = syn_name2id((char_u *)"Normal");
7930 if (id > 0)
7931 {
7932 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7933 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7934 }
7935 }
7936}
7937
7938/*
7939 * Return the handle for a color name.
7940 * Returns INVALCOLOR when failed.
7941 */
7942 static guicolor_T
7943color_name2handle(name)
7944 char_u *name;
7945{
7946 if (STRCMP(name, "NONE") == 0)
7947 return INVALCOLOR;
7948
7949 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7950 return gui.norm_pixel;
7951 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7952 return gui.back_pixel;
7953
7954 return gui_get_color(name);
7955}
7956
7957/*
7958 * Return the handle for a font name.
7959 * Returns NOFONT when failed.
7960 */
7961 static GuiFont
7962font_name2handle(name)
7963 char_u *name;
7964{
7965 if (STRCMP(name, "NONE") == 0)
7966 return NOFONT;
7967
7968 return gui_mch_get_font(name, TRUE);
7969}
7970
7971# ifdef FEAT_XFONTSET
7972/*
7973 * Return the handle for a fontset name.
7974 * Returns NOFONTSET when failed.
7975 */
7976 static GuiFontset
7977fontset_name2handle(name, fixed_width)
7978 char_u *name;
7979 int fixed_width;
7980{
7981 if (STRCMP(name, "NONE") == 0)
7982 return NOFONTSET;
7983
7984 return gui_mch_get_fontset(name, TRUE, fixed_width);
7985}
7986# endif
7987
7988/*
7989 * Get the font or fontset for one highlight group.
7990 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007991 static void
7992hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
7993 int idx;
7994 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00007995 int do_normal; /* set normal font */
7996 int do_menu UNUSED; /* set menu font */
7997 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007998{
7999# ifdef FEAT_XFONTSET
8000 /* If 'guifontset' is not empty, first try using the name as a
8001 * fontset. If that doesn't work, use it as a font name. */
8002 if (*p_guifontset != NUL
8003# ifdef FONTSET_ALWAYS
8004 || do_menu
8005# endif
8006# ifdef FEAT_BEVAL_TIP
8007 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8008 || do_tooltip
8009# endif
8010 )
8011 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8012# ifdef FONTSET_ALWAYS
8013 || do_menu
8014# endif
8015# ifdef FEAT_BEVAL_TIP
8016 || do_tooltip
8017# endif
8018 );
8019 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8020 {
8021 /* If it worked and it's the Normal group, use it as the
8022 * normal fontset. Same for the Menu group. */
8023 if (do_normal)
8024 gui_init_font(arg, TRUE);
8025# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8026 if (do_menu)
8027 {
8028# ifdef FONTSET_ALWAYS
8029 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8030# else
8031 /* YIKES! This is a bug waiting to crash the program */
8032 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8033# endif
8034 gui_mch_new_menu_font();
8035 }
8036# ifdef FEAT_BEVAL
8037 if (do_tooltip)
8038 {
8039 /* The Athena widget set cannot currently handle switching between
8040 * displaying a single font and a fontset.
8041 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008042 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008043 * XFontStruct is used.
8044 */
8045 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8046 gui_mch_new_tooltip_font();
8047 }
8048# endif
8049# endif
8050 }
8051 else
8052# endif
8053 {
8054 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8055 /* If it worked and it's the Normal group, use it as the
8056 * normal font. Same for the Menu group. */
8057 if (HL_TABLE()[idx].sg_font != NOFONT)
8058 {
8059 if (do_normal)
8060 gui_init_font(arg, FALSE);
8061#ifndef FONTSET_ALWAYS
8062# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8063 if (do_menu)
8064 {
8065 gui.menu_font = HL_TABLE()[idx].sg_font;
8066 gui_mch_new_menu_font();
8067 }
8068# endif
8069#endif
8070 }
8071 }
8072}
8073
8074#endif /* FEAT_GUI */
8075
8076/*
8077 * Table with the specifications for an attribute number.
8078 * Note that this table is used by ALL buffers. This is required because the
8079 * GUI can redraw at any time for any buffer.
8080 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008081static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008082
8083#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8084
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008085static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008086
8087#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8088
8089#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008090static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008091
8092#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8093#endif
8094
8095/*
8096 * Return the attr number for a set of colors and font.
8097 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8098 * if the combination is new.
8099 * Return 0 for error (no more room).
8100 */
8101 static int
8102get_attr_entry(table, aep)
8103 garray_T *table;
8104 attrentry_T *aep;
8105{
8106 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008107 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008108 static int recursive = FALSE;
8109
8110 /*
8111 * Init the table, in case it wasn't done yet.
8112 */
8113 table->ga_itemsize = sizeof(attrentry_T);
8114 table->ga_growsize = 7;
8115
8116 /*
8117 * Try to find an entry with the same specifications.
8118 */
8119 for (i = 0; i < table->ga_len; ++i)
8120 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008121 taep = &(((attrentry_T *)table->ga_data)[i]);
8122 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008123 && (
8124#ifdef FEAT_GUI
8125 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008126 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8127 && aep->ae_u.gui.bg_color
8128 == taep->ae_u.gui.bg_color
8129 && aep->ae_u.gui.sp_color
8130 == taep->ae_u.gui.sp_color
8131 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008132# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008133 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008134# endif
8135 ))
8136 ||
8137#endif
8138 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008139 && (aep->ae_u.term.start == NULL)
8140 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008141 && (aep->ae_u.term.start == NULL
8142 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008143 taep->ae_u.term.start) == 0)
8144 && (aep->ae_u.term.stop == NULL)
8145 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008146 && (aep->ae_u.term.stop == NULL
8147 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008148 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008149 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008150 && aep->ae_u.cterm.fg_color
8151 == taep->ae_u.cterm.fg_color
8152 && aep->ae_u.cterm.bg_color
8153 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008154 ))
8155
8156 return i + ATTR_OFF;
8157 }
8158
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008159 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008160 {
8161 /*
8162 * Running out of attribute entries! remove all attributes, and
8163 * compute new ones for all groups.
8164 * When called recursively, we are really out of numbers.
8165 */
8166 if (recursive)
8167 {
8168 EMSG(_("E424: Too many different highlighting attributes in use"));
8169 return 0;
8170 }
8171 recursive = TRUE;
8172
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008173 clear_hl_tables();
8174
Bram Moolenaar071d4272004-06-13 20:20:40 +00008175 must_redraw = CLEAR;
8176
8177 for (i = 0; i < highlight_ga.ga_len; ++i)
8178 set_hl_attr(i);
8179
8180 recursive = FALSE;
8181 }
8182
8183 /*
8184 * This is a new combination of colors and font, add an entry.
8185 */
8186 if (ga_grow(table, 1) == FAIL)
8187 return 0;
8188
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008189 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8190 vim_memset(taep, 0, sizeof(attrentry_T));
8191 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008192#ifdef FEAT_GUI
8193 if (table == &gui_attr_table)
8194 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008195 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8196 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8197 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8198 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008199# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008200 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008201# endif
8202 }
8203#endif
8204 if (table == &term_attr_table)
8205 {
8206 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008207 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008208 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008209 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008210 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008211 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008212 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008213 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008214 }
8215 else if (table == &cterm_attr_table)
8216 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008217 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8218 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008219 }
8220 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008221 return (table->ga_len - 1 + ATTR_OFF);
8222}
8223
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008224/*
8225 * Clear all highlight tables.
8226 */
8227 void
8228clear_hl_tables()
8229{
8230 int i;
8231 attrentry_T *taep;
8232
8233#ifdef FEAT_GUI
8234 ga_clear(&gui_attr_table);
8235#endif
8236 for (i = 0; i < term_attr_table.ga_len; ++i)
8237 {
8238 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8239 vim_free(taep->ae_u.term.start);
8240 vim_free(taep->ae_u.term.stop);
8241 }
8242 ga_clear(&term_attr_table);
8243 ga_clear(&cterm_attr_table);
8244}
8245
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008246#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008247/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008248 * Combine special attributes (e.g., for spelling) with other attributes
8249 * (e.g., for syntax highlighting).
8250 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008251 * This creates a new group when required.
8252 * Since we expect there to be few spelling mistakes we don't cache the
8253 * result.
8254 * Return the resulting attributes.
8255 */
8256 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008257hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008258 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008259 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008260{
8261 attrentry_T *char_aep = NULL;
8262 attrentry_T *spell_aep;
8263 attrentry_T new_en;
8264
8265 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008266 return prim_attr;
8267 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8268 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008269#ifdef FEAT_GUI
8270 if (gui.in_use)
8271 {
8272 if (char_attr > HL_ALL)
8273 char_aep = syn_gui_attr2entry(char_attr);
8274 if (char_aep != NULL)
8275 new_en = *char_aep;
8276 else
8277 {
8278 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008279 new_en.ae_u.gui.fg_color = INVALCOLOR;
8280 new_en.ae_u.gui.bg_color = INVALCOLOR;
8281 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008282 if (char_attr <= HL_ALL)
8283 new_en.ae_attr = char_attr;
8284 }
8285
Bram Moolenaar30abd282005-06-22 22:35:10 +00008286 if (prim_attr <= HL_ALL)
8287 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008288 else
8289 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008290 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008291 if (spell_aep != NULL)
8292 {
8293 new_en.ae_attr |= spell_aep->ae_attr;
8294 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8295 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8296 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8297 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8298 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8299 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8300 if (spell_aep->ae_u.gui.font != NOFONT)
8301 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8302# ifdef FEAT_XFONTSET
8303 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8304 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8305# endif
8306 }
8307 }
8308 return get_attr_entry(&gui_attr_table, &new_en);
8309 }
8310#endif
8311
8312 if (t_colors > 1)
8313 {
8314 if (char_attr > HL_ALL)
8315 char_aep = syn_cterm_attr2entry(char_attr);
8316 if (char_aep != NULL)
8317 new_en = *char_aep;
8318 else
8319 {
8320 vim_memset(&new_en, 0, sizeof(new_en));
8321 if (char_attr <= HL_ALL)
8322 new_en.ae_attr = char_attr;
8323 }
8324
Bram Moolenaar30abd282005-06-22 22:35:10 +00008325 if (prim_attr <= HL_ALL)
8326 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008327 else
8328 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008329 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008330 if (spell_aep != NULL)
8331 {
8332 new_en.ae_attr |= spell_aep->ae_attr;
8333 if (spell_aep->ae_u.cterm.fg_color > 0)
8334 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8335 if (spell_aep->ae_u.cterm.bg_color > 0)
8336 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8337 }
8338 }
8339 return get_attr_entry(&cterm_attr_table, &new_en);
8340 }
8341
8342 if (char_attr > HL_ALL)
8343 char_aep = syn_term_attr2entry(char_attr);
8344 if (char_aep != NULL)
8345 new_en = *char_aep;
8346 else
8347 {
8348 vim_memset(&new_en, 0, sizeof(new_en));
8349 if (char_attr <= HL_ALL)
8350 new_en.ae_attr = char_attr;
8351 }
8352
Bram Moolenaar30abd282005-06-22 22:35:10 +00008353 if (prim_attr <= HL_ALL)
8354 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008355 else
8356 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008357 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008358 if (spell_aep != NULL)
8359 {
8360 new_en.ae_attr |= spell_aep->ae_attr;
8361 if (spell_aep->ae_u.term.start != NULL)
8362 {
8363 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8364 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8365 }
8366 }
8367 }
8368 return get_attr_entry(&term_attr_table, &new_en);
8369}
8370#endif
8371
Bram Moolenaar071d4272004-06-13 20:20:40 +00008372#ifdef FEAT_GUI
8373
8374 attrentry_T *
8375syn_gui_attr2entry(attr)
8376 int attr;
8377{
8378 attr -= ATTR_OFF;
8379 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8380 return NULL;
8381 return &(GUI_ATTR_ENTRY(attr));
8382}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008383#endif /* FEAT_GUI */
8384
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008385/*
8386 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8387 * Only to be used when "attr" > HL_ALL.
8388 */
8389 int
8390syn_attr2attr(attr)
8391 int attr;
8392{
8393 attrentry_T *aep;
8394
8395#ifdef FEAT_GUI
8396 if (gui.in_use)
8397 aep = syn_gui_attr2entry(attr);
8398 else
8399#endif
8400 if (t_colors > 1)
8401 aep = syn_cterm_attr2entry(attr);
8402 else
8403 aep = syn_term_attr2entry(attr);
8404
8405 if (aep == NULL) /* highlighting not set */
8406 return 0;
8407 return aep->ae_attr;
8408}
8409
8410
Bram Moolenaar071d4272004-06-13 20:20:40 +00008411 attrentry_T *
8412syn_term_attr2entry(attr)
8413 int attr;
8414{
8415 attr -= ATTR_OFF;
8416 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8417 return NULL;
8418 return &(TERM_ATTR_ENTRY(attr));
8419}
8420
8421 attrentry_T *
8422syn_cterm_attr2entry(attr)
8423 int attr;
8424{
8425 attr -= ATTR_OFF;
8426 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8427 return NULL;
8428 return &(CTERM_ATTR_ENTRY(attr));
8429}
8430
8431#define LIST_ATTR 1
8432#define LIST_STRING 2
8433#define LIST_INT 3
8434
8435 static void
8436highlight_list_one(id)
8437 int id;
8438{
8439 struct hl_group *sgp;
8440 int didh = FALSE;
8441
8442 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8443
8444 didh = highlight_list_arg(id, didh, LIST_ATTR,
8445 sgp->sg_term, NULL, "term");
8446 didh = highlight_list_arg(id, didh, LIST_STRING,
8447 0, sgp->sg_start, "start");
8448 didh = highlight_list_arg(id, didh, LIST_STRING,
8449 0, sgp->sg_stop, "stop");
8450
8451 didh = highlight_list_arg(id, didh, LIST_ATTR,
8452 sgp->sg_cterm, NULL, "cterm");
8453 didh = highlight_list_arg(id, didh, LIST_INT,
8454 sgp->sg_cterm_fg, NULL, "ctermfg");
8455 didh = highlight_list_arg(id, didh, LIST_INT,
8456 sgp->sg_cterm_bg, NULL, "ctermbg");
8457
Bram Moolenaar61623362010-07-14 22:04:22 +02008458#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008459 didh = highlight_list_arg(id, didh, LIST_ATTR,
8460 sgp->sg_gui, NULL, "gui");
8461 didh = highlight_list_arg(id, didh, LIST_STRING,
8462 0, sgp->sg_gui_fg_name, "guifg");
8463 didh = highlight_list_arg(id, didh, LIST_STRING,
8464 0, sgp->sg_gui_bg_name, "guibg");
8465 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008466 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008467#endif
8468#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008469 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008470 0, sgp->sg_font_name, "font");
8471#endif
8472
Bram Moolenaar661b1822005-07-28 22:36:45 +00008473 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008474 {
8475 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008476 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008477 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8478 msg_putchar(' ');
8479 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8480 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008481
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008482 if (!didh)
8483 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008484#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008485 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008486 last_set_msg(sgp->sg_scriptID);
8487#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008488}
8489
8490 static int
8491highlight_list_arg(id, didh, type, iarg, sarg, name)
8492 int id;
8493 int didh;
8494 int type;
8495 int iarg;
8496 char_u *sarg;
8497 char *name;
8498{
8499 char_u buf[100];
8500 char_u *ts;
8501 int i;
8502
Bram Moolenaar661b1822005-07-28 22:36:45 +00008503 if (got_int)
8504 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008505 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8506 {
8507 ts = buf;
8508 if (type == LIST_INT)
8509 sprintf((char *)buf, "%d", iarg - 1);
8510 else if (type == LIST_STRING)
8511 ts = sarg;
8512 else /* type == LIST_ATTR */
8513 {
8514 buf[0] = NUL;
8515 for (i = 0; hl_attr_table[i] != 0; ++i)
8516 {
8517 if (iarg & hl_attr_table[i])
8518 {
8519 if (buf[0] != NUL)
8520 STRCAT(buf, ",");
8521 STRCAT(buf, hl_name_table[i]);
8522 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8523 }
8524 }
8525 }
8526
8527 (void)syn_list_header(didh,
8528 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8529 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008530 if (!got_int)
8531 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008532 if (*name != NUL)
8533 {
8534 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8535 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8536 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008537 msg_outtrans(ts);
8538 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008539 }
8540 return didh;
8541}
8542
8543#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8544/*
8545 * Return "1" if highlight group "id" has attribute "flag".
8546 * Return NULL otherwise.
8547 */
8548 char_u *
8549highlight_has_attr(id, flag, modec)
8550 int id;
8551 int flag;
8552 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8553{
8554 int attr;
8555
8556 if (id <= 0 || id > highlight_ga.ga_len)
8557 return NULL;
8558
Bram Moolenaar61623362010-07-14 22:04:22 +02008559#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008560 if (modec == 'g')
8561 attr = HL_TABLE()[id - 1].sg_gui;
8562 else
8563#endif
8564 if (modec == 'c')
8565 attr = HL_TABLE()[id - 1].sg_cterm;
8566 else
8567 attr = HL_TABLE()[id - 1].sg_term;
8568
8569 if (attr & flag)
8570 return (char_u *)"1";
8571 return NULL;
8572}
8573#endif
8574
8575#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8576/*
8577 * Return color name of highlight group "id".
8578 */
8579 char_u *
8580highlight_color(id, what, modec)
8581 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008582 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008583 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8584{
8585 static char_u name[20];
8586 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008587 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008588 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008589 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008590
8591 if (id <= 0 || id > highlight_ga.ga_len)
8592 return NULL;
8593
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008594 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008595 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008596 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008597 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008598 font = TRUE;
8599 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008600 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008601 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8602 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008603 if (modec == 'g')
8604 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008605# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008606 /* return font name */
8607 if (font)
8608 return HL_TABLE()[id - 1].sg_font_name;
8609
Bram Moolenaar071d4272004-06-13 20:20:40 +00008610 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008611 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008612 {
8613 guicolor_T color;
8614 long_u rgb;
8615 static char_u buf[10];
8616
8617 if (fg)
8618 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008619 else if (sp)
8620 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008621 else
8622 color = HL_TABLE()[id - 1].sg_gui_bg;
8623 if (color == INVALCOLOR)
8624 return NULL;
8625 rgb = gui_mch_get_rgb(color);
8626 sprintf((char *)buf, "#%02x%02x%02x",
8627 (unsigned)(rgb >> 16),
8628 (unsigned)(rgb >> 8) & 255,
8629 (unsigned)rgb & 255);
8630 return buf;
8631 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008632#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008633 if (fg)
8634 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008635 if (sp)
8636 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008637 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8638 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008639 if (font || sp)
8640 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008641 if (modec == 'c')
8642 {
8643 if (fg)
8644 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8645 else
8646 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8647 sprintf((char *)name, "%d", n);
8648 return name;
8649 }
8650 /* term doesn't have color */
8651 return NULL;
8652}
8653#endif
8654
8655#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8656 || defined(PROTO)
8657/*
8658 * Return color name of highlight group "id" as RGB value.
8659 */
8660 long_u
8661highlight_gui_color_rgb(id, fg)
8662 int id;
8663 int fg; /* TRUE = fg, FALSE = bg */
8664{
8665 guicolor_T color;
8666
8667 if (id <= 0 || id > highlight_ga.ga_len)
8668 return 0L;
8669
8670 if (fg)
8671 color = HL_TABLE()[id - 1].sg_gui_fg;
8672 else
8673 color = HL_TABLE()[id - 1].sg_gui_bg;
8674
8675 if (color == INVALCOLOR)
8676 return 0L;
8677
8678 return gui_mch_get_rgb(color);
8679}
8680#endif
8681
8682/*
8683 * Output the syntax list header.
8684 * Return TRUE when started a new line.
8685 */
8686 static int
8687syn_list_header(did_header, outlen, id)
8688 int did_header; /* did header already */
8689 int outlen; /* length of string that comes */
8690 int id; /* highlight group id */
8691{
8692 int endcol = 19;
8693 int newline = TRUE;
8694
8695 if (!did_header)
8696 {
8697 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008698 if (got_int)
8699 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008700 msg_outtrans(HL_TABLE()[id - 1].sg_name);
8701 endcol = 15;
8702 }
8703 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008704 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008705 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008706 if (got_int)
8707 return TRUE;
8708 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008709 else
8710 {
8711 if (msg_col >= endcol) /* wrap around is like starting a new line */
8712 newline = FALSE;
8713 }
8714
8715 if (msg_col >= endcol) /* output at least one space */
8716 endcol = msg_col + 1;
8717 if (Columns <= endcol) /* avoid hang for tiny window */
8718 endcol = Columns - 1;
8719
8720 msg_advance(endcol);
8721
8722 /* Show "xxx" with the attributes. */
8723 if (!did_header)
8724 {
8725 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8726 msg_putchar(' ');
8727 }
8728
8729 return newline;
8730}
8731
8732/*
8733 * Set the attribute numbers for a highlight group.
8734 * Called after one of the attributes has changed.
8735 */
8736 static void
8737set_hl_attr(idx)
8738 int idx; /* index in array */
8739{
8740 attrentry_T at_en;
8741 struct hl_group *sgp = HL_TABLE() + idx;
8742
8743 /* The "Normal" group doesn't need an attribute number */
8744 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8745 return;
8746
8747#ifdef FEAT_GUI
8748 /*
8749 * For the GUI mode: If there are other than "normal" highlighting
8750 * attributes, need to allocate an attr number.
8751 */
8752 if (sgp->sg_gui_fg == INVALCOLOR
8753 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008754 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008755 && sgp->sg_font == NOFONT
8756# ifdef FEAT_XFONTSET
8757 && sgp->sg_fontset == NOFONTSET
8758# endif
8759 )
8760 {
8761 sgp->sg_gui_attr = sgp->sg_gui;
8762 }
8763 else
8764 {
8765 at_en.ae_attr = sgp->sg_gui;
8766 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8767 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008768 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008769 at_en.ae_u.gui.font = sgp->sg_font;
8770# ifdef FEAT_XFONTSET
8771 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8772# endif
8773 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8774 }
8775#endif
8776 /*
8777 * For the term mode: If there are other than "normal" highlighting
8778 * attributes, need to allocate an attr number.
8779 */
8780 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8781 sgp->sg_term_attr = sgp->sg_term;
8782 else
8783 {
8784 at_en.ae_attr = sgp->sg_term;
8785 at_en.ae_u.term.start = sgp->sg_start;
8786 at_en.ae_u.term.stop = sgp->sg_stop;
8787 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8788 }
8789
8790 /*
8791 * For the color term mode: If there are other than "normal"
8792 * highlighting attributes, need to allocate an attr number.
8793 */
8794 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8795 sgp->sg_cterm_attr = sgp->sg_cterm;
8796 else
8797 {
8798 at_en.ae_attr = sgp->sg_cterm;
8799 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8800 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8801 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8802 }
8803}
8804
8805/*
8806 * Lookup a highlight group name and return it's ID.
8807 * If it is not found, 0 is returned.
8808 */
8809 int
8810syn_name2id(name)
8811 char_u *name;
8812{
8813 int i;
8814 char_u name_u[200];
8815
8816 /* Avoid using stricmp() too much, it's slow on some systems */
8817 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8818 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00008819 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008820 vim_strup(name_u);
8821 for (i = highlight_ga.ga_len; --i >= 0; )
8822 if (HL_TABLE()[i].sg_name_u != NULL
8823 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8824 break;
8825 return i + 1;
8826}
8827
8828#if defined(FEAT_EVAL) || defined(PROTO)
8829/*
8830 * Return TRUE if highlight group "name" exists.
8831 */
8832 int
8833highlight_exists(name)
8834 char_u *name;
8835{
8836 return (syn_name2id(name) > 0);
8837}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008838
8839# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
8840/*
8841 * Return the name of highlight group "id".
8842 * When not a valid ID return an empty string.
8843 */
8844 char_u *
8845syn_id2name(id)
8846 int id;
8847{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00008848 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008849 return (char_u *)"";
8850 return HL_TABLE()[id - 1].sg_name;
8851}
8852# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008853#endif
8854
8855/*
8856 * Like syn_name2id(), but take a pointer + length argument.
8857 */
8858 int
8859syn_namen2id(linep, len)
8860 char_u *linep;
8861 int len;
8862{
8863 char_u *name;
8864 int id = 0;
8865
8866 name = vim_strnsave(linep, len);
8867 if (name != NULL)
8868 {
8869 id = syn_name2id(name);
8870 vim_free(name);
8871 }
8872 return id;
8873}
8874
8875/*
8876 * Find highlight group name in the table and return it's ID.
8877 * The argument is a pointer to the name and the length of the name.
8878 * If it doesn't exist yet, a new entry is created.
8879 * Return 0 for failure.
8880 */
8881 int
8882syn_check_group(pp, len)
8883 char_u *pp;
8884 int len;
8885{
8886 int id;
8887 char_u *name;
8888
8889 name = vim_strnsave(pp, len);
8890 if (name == NULL)
8891 return 0;
8892
8893 id = syn_name2id(name);
8894 if (id == 0) /* doesn't exist yet */
8895 id = syn_add_group(name);
8896 else
8897 vim_free(name);
8898 return id;
8899}
8900
8901/*
8902 * Add new highlight group and return it's ID.
8903 * "name" must be an allocated string, it will be consumed.
8904 * Return 0 for failure.
8905 */
8906 static int
8907syn_add_group(name)
8908 char_u *name;
8909{
8910 char_u *p;
8911
8912 /* Check that the name is ASCII letters, digits and underscore. */
8913 for (p = name; *p != NUL; ++p)
8914 {
8915 if (!vim_isprintc(*p))
8916 {
8917 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008918 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008919 return 0;
8920 }
8921 else if (!ASCII_ISALNUM(*p) && *p != '_')
8922 {
8923 /* This is an error, but since there previously was no check only
8924 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008925 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008926 MSG(_("W18: Invalid character in group name"));
8927 break;
8928 }
8929 }
8930
8931 /*
8932 * First call for this growarray: init growing array.
8933 */
8934 if (highlight_ga.ga_data == NULL)
8935 {
8936 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8937 highlight_ga.ga_growsize = 10;
8938 }
8939
8940 /*
8941 * Make room for at least one other syntax_highlight entry.
8942 */
8943 if (ga_grow(&highlight_ga, 1) == FAIL)
8944 {
8945 vim_free(name);
8946 return 0;
8947 }
8948
8949 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8950 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8951 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8952#ifdef FEAT_GUI
8953 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8954 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008955 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008956#endif
8957 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008958
8959 return highlight_ga.ga_len; /* ID is index plus one */
8960}
8961
8962/*
8963 * When, just after calling syn_add_group(), an error is discovered, this
8964 * function deletes the new name.
8965 */
8966 static void
8967syn_unadd_group()
8968{
8969 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008970 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8971 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8972}
8973
8974/*
8975 * Translate a group ID to highlight attributes.
8976 */
8977 int
8978syn_id2attr(hl_id)
8979 int hl_id;
8980{
8981 int attr;
8982 struct hl_group *sgp;
8983
8984 hl_id = syn_get_final_id(hl_id);
8985 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8986
8987#ifdef FEAT_GUI
8988 /*
8989 * Only use GUI attr when the GUI is being used.
8990 */
8991 if (gui.in_use)
8992 attr = sgp->sg_gui_attr;
8993 else
8994#endif
8995 if (t_colors > 1)
8996 attr = sgp->sg_cterm_attr;
8997 else
8998 attr = sgp->sg_term_attr;
8999
9000 return attr;
9001}
9002
9003#ifdef FEAT_GUI
9004/*
9005 * Get the GUI colors and attributes for a group ID.
9006 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9007 */
9008 int
9009syn_id2colors(hl_id, fgp, bgp)
9010 int hl_id;
9011 guicolor_T *fgp;
9012 guicolor_T *bgp;
9013{
9014 struct hl_group *sgp;
9015
9016 hl_id = syn_get_final_id(hl_id);
9017 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9018
9019 *fgp = sgp->sg_gui_fg;
9020 *bgp = sgp->sg_gui_bg;
9021 return sgp->sg_gui;
9022}
9023#endif
9024
9025/*
9026 * Translate a group ID to the final group ID (following links).
9027 */
9028 int
9029syn_get_final_id(hl_id)
9030 int hl_id;
9031{
9032 int count;
9033 struct hl_group *sgp;
9034
9035 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9036 return 0; /* Can be called from eval!! */
9037
9038 /*
9039 * Follow links until there is no more.
9040 * Look out for loops! Break after 100 links.
9041 */
9042 for (count = 100; --count >= 0; )
9043 {
9044 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9045 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9046 break;
9047 hl_id = sgp->sg_link;
9048 }
9049
9050 return hl_id;
9051}
9052
9053#ifdef FEAT_GUI
9054/*
9055 * Call this function just after the GUI has started.
9056 * It finds the font and color handles for the highlighting groups.
9057 */
9058 void
9059highlight_gui_started()
9060{
9061 int idx;
9062
9063 /* First get the colors from the "Normal" and "Menu" group, if set */
9064 set_normal_colors();
9065
9066 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9067 gui_do_one_color(idx, FALSE, FALSE);
9068
9069 highlight_changed();
9070}
9071
9072 static void
9073gui_do_one_color(idx, do_menu, do_tooltip)
9074 int idx;
9075 int do_menu; /* TRUE: might set the menu font */
9076 int do_tooltip; /* TRUE: might set the tooltip font */
9077{
9078 int didit = FALSE;
9079
9080 if (HL_TABLE()[idx].sg_font_name != NULL)
9081 {
9082 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
9083 do_tooltip);
9084 didit = TRUE;
9085 }
9086 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9087 {
9088 HL_TABLE()[idx].sg_gui_fg =
9089 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9090 didit = TRUE;
9091 }
9092 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9093 {
9094 HL_TABLE()[idx].sg_gui_bg =
9095 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9096 didit = TRUE;
9097 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009098 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9099 {
9100 HL_TABLE()[idx].sg_gui_sp =
9101 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9102 didit = TRUE;
9103 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009104 if (didit) /* need to get a new attr number */
9105 set_hl_attr(idx);
9106}
9107
9108#endif
9109
9110/*
9111 * Translate the 'highlight' option into attributes in highlight_attr[] and
9112 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9113 * corresponding highlights to use on top of HLF_SNC is computed.
9114 * Called only when the 'highlight' option has been changed and upon first
9115 * screen redraw after any :highlight command.
9116 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9117 */
9118 int
9119highlight_changed()
9120{
9121 int hlf;
9122 int i;
9123 char_u *p;
9124 int attr;
9125 char_u *end;
9126 int id;
9127#ifdef USER_HIGHLIGHT
9128 char_u userhl[10];
9129# ifdef FEAT_STL_OPT
9130 int id_SNC = -1;
9131 int id_S = -1;
9132 int hlcnt;
9133# endif
9134#endif
9135 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9136
9137 need_highlight_changed = FALSE;
9138
9139 /*
9140 * Clear all attributes.
9141 */
9142 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9143 highlight_attr[hlf] = 0;
9144
9145 /*
9146 * First set all attributes to their default value.
9147 * Then use the attributes from the 'highlight' option.
9148 */
9149 for (i = 0; i < 2; ++i)
9150 {
9151 if (i)
9152 p = p_hl;
9153 else
9154 p = get_highlight_default();
9155 if (p == NULL) /* just in case */
9156 continue;
9157
9158 while (*p)
9159 {
9160 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9161 if (hl_flags[hlf] == *p)
9162 break;
9163 ++p;
9164 if (hlf == (int)HLF_COUNT || *p == NUL)
9165 return FAIL;
9166
9167 /*
9168 * Allow several hl_flags to be combined, like "bu" for
9169 * bold-underlined.
9170 */
9171 attr = 0;
9172 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9173 {
9174 if (vim_iswhite(*p)) /* ignore white space */
9175 continue;
9176
9177 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9178 return FAIL;
9179
9180 switch (*p)
9181 {
9182 case 'b': attr |= HL_BOLD;
9183 break;
9184 case 'i': attr |= HL_ITALIC;
9185 break;
9186 case '-':
9187 case 'n': /* no highlighting */
9188 break;
9189 case 'r': attr |= HL_INVERSE;
9190 break;
9191 case 's': attr |= HL_STANDOUT;
9192 break;
9193 case 'u': attr |= HL_UNDERLINE;
9194 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009195 case 'c': attr |= HL_UNDERCURL;
9196 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009197 case ':': ++p; /* highlight group name */
9198 if (attr || *p == NUL) /* no combinations */
9199 return FAIL;
9200 end = vim_strchr(p, ',');
9201 if (end == NULL)
9202 end = p + STRLEN(p);
9203 id = syn_check_group(p, (int)(end - p));
9204 if (id == 0)
9205 return FAIL;
9206 attr = syn_id2attr(id);
9207 p = end - 1;
9208#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9209 if (hlf == (int)HLF_SNC)
9210 id_SNC = syn_get_final_id(id);
9211 else if (hlf == (int)HLF_S)
9212 id_S = syn_get_final_id(id);
9213#endif
9214 break;
9215 default: return FAIL;
9216 }
9217 }
9218 highlight_attr[hlf] = attr;
9219
9220 p = skip_to_option_part(p); /* skip comma and spaces */
9221 }
9222 }
9223
9224#ifdef USER_HIGHLIGHT
9225 /* Setup the user highlights
9226 *
9227 * Temporarily utilize 10 more hl entries. Have to be in there
9228 * simultaneously in case of table overflows in get_attr_entry()
9229 */
9230# ifdef FEAT_STL_OPT
9231 if (ga_grow(&highlight_ga, 10) == FAIL)
9232 return FAIL;
9233 hlcnt = highlight_ga.ga_len;
9234 if (id_S == 0)
9235 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009236 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009237 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9238 id_S = hlcnt + 10;
9239 }
9240# endif
9241 for (i = 0; i < 9; i++)
9242 {
9243 sprintf((char *)userhl, "User%d", i + 1);
9244 id = syn_name2id(userhl);
9245 if (id == 0)
9246 {
9247 highlight_user[i] = 0;
9248# ifdef FEAT_STL_OPT
9249 highlight_stlnc[i] = 0;
9250# endif
9251 }
9252 else
9253 {
9254# ifdef FEAT_STL_OPT
9255 struct hl_group *hlt = HL_TABLE();
9256# endif
9257
9258 highlight_user[i] = syn_id2attr(id);
9259# ifdef FEAT_STL_OPT
9260 if (id_SNC == 0)
9261 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009262 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009263 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9264 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009265# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009266 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9267# endif
9268 }
9269 else
9270 mch_memmove(&hlt[hlcnt + i],
9271 &hlt[id_SNC - 1],
9272 sizeof(struct hl_group));
9273 hlt[hlcnt + i].sg_link = 0;
9274
9275 /* Apply difference between UserX and HLF_S to HLF_SNC */
9276 hlt[hlcnt + i].sg_term ^=
9277 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9278 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9279 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9280 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9281 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9282 hlt[hlcnt + i].sg_cterm ^=
9283 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9284 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9285 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9286 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9287 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009288# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009289 hlt[hlcnt + i].sg_gui ^=
9290 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009291# endif
9292# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009293 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9294 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9295 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9296 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009297 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9298 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009299 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9300 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9301# ifdef FEAT_XFONTSET
9302 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9303 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9304# endif
9305# endif
9306 highlight_ga.ga_len = hlcnt + i + 1;
9307 set_hl_attr(hlcnt + i); /* At long last we can apply */
9308 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9309# endif
9310 }
9311 }
9312# ifdef FEAT_STL_OPT
9313 highlight_ga.ga_len = hlcnt;
9314# endif
9315
9316#endif /* USER_HIGHLIGHT */
9317
9318 return OK;
9319}
9320
Bram Moolenaar4f688582007-07-24 12:34:30 +00009321#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009322
9323static void highlight_list __ARGS((void));
9324static void highlight_list_two __ARGS((int cnt, int attr));
9325
9326/*
9327 * Handle command line completion for :highlight command.
9328 */
9329 void
9330set_context_in_highlight_cmd(xp, arg)
9331 expand_T *xp;
9332 char_u *arg;
9333{
9334 char_u *p;
9335
9336 /* Default: expand group names */
9337 xp->xp_context = EXPAND_HIGHLIGHT;
9338 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009339 include_link = 2;
9340 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009341
9342 /* (part of) subcommand already typed */
9343 if (*arg != NUL)
9344 {
9345 p = skiptowhite(arg);
9346 if (*p != NUL) /* past "default" or group name */
9347 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009348 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009349 if (STRNCMP("default", arg, p - arg) == 0)
9350 {
9351 arg = skipwhite(p);
9352 xp->xp_pattern = arg;
9353 p = skiptowhite(arg);
9354 }
9355 if (*p != NUL) /* past group name */
9356 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009357 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009358 if (arg[1] == 'i' && arg[0] == 'N')
9359 highlight_list();
9360 if (STRNCMP("link", arg, p - arg) == 0
9361 || STRNCMP("clear", arg, p - arg) == 0)
9362 {
9363 xp->xp_pattern = skipwhite(p);
9364 p = skiptowhite(xp->xp_pattern);
9365 if (*p != NUL) /* past first group name */
9366 {
9367 xp->xp_pattern = skipwhite(p);
9368 p = skiptowhite(xp->xp_pattern);
9369 }
9370 }
9371 if (*p != NUL) /* past group name(s) */
9372 xp->xp_context = EXPAND_NOTHING;
9373 }
9374 }
9375 }
9376}
9377
9378/*
9379 * List highlighting matches in a nice way.
9380 */
9381 static void
9382highlight_list()
9383{
9384 int i;
9385
9386 for (i = 10; --i >= 0; )
9387 highlight_list_two(i, hl_attr(HLF_D));
9388 for (i = 40; --i >= 0; )
9389 highlight_list_two(99, 0);
9390}
9391
9392 static void
9393highlight_list_two(cnt, attr)
9394 int cnt;
9395 int attr;
9396{
9397 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
9398 msg_clr_eos();
9399 out_flush();
9400 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9401}
9402
9403#endif /* FEAT_CMDL_COMPL */
9404
9405#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9406 || defined(FEAT_SIGNS) || defined(PROTO)
9407/*
9408 * Function given to ExpandGeneric() to obtain the list of group names.
9409 * Also used for synIDattr() function.
9410 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009411 char_u *
9412get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009413 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009414 int idx;
9415{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009416#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009417 if (idx == highlight_ga.ga_len && include_none != 0)
9418 return (char_u *)"none";
9419 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009420 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009421 if (idx == highlight_ga.ga_len + include_none + include_default
9422 && include_link != 0)
9423 return (char_u *)"link";
9424 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9425 && include_link != 0)
9426 return (char_u *)"clear";
9427#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009428 if (idx < 0 || idx >= highlight_ga.ga_len)
9429 return NULL;
9430 return HL_TABLE()[idx].sg_name;
9431}
9432#endif
9433
Bram Moolenaar4f688582007-07-24 12:34:30 +00009434#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009435/*
9436 * Free all the highlight group fonts.
9437 * Used when quitting for systems which need it.
9438 */
9439 void
9440free_highlight_fonts()
9441{
9442 int idx;
9443
9444 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9445 {
9446 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9447 HL_TABLE()[idx].sg_font = NOFONT;
9448# ifdef FEAT_XFONTSET
9449 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9450 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9451# endif
9452 }
9453
9454 gui_mch_free_font(gui.norm_font);
9455# ifdef FEAT_XFONTSET
9456 gui_mch_free_fontset(gui.fontset);
9457# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009458# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009459 gui_mch_free_font(gui.bold_font);
9460 gui_mch_free_font(gui.ital_font);
9461 gui_mch_free_font(gui.boldital_font);
9462# endif
9463}
9464#endif
9465
9466/**************************************
9467 * End of Highlighting stuff *
9468 **************************************/