blob: 4655c77660e2b28a1fb1725f05c6a8df5d63b736 [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
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200199static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200200static int current_flags = 0;
201static int current_sub_char = 0;
202#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000203
Bram Moolenaar217ad922005-03-20 22:37:15 +0000204typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000205{
206 char_u *scl_name; /* syntax cluster name */
207 char_u *scl_name_u; /* uppercase of scl_name */
208 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000209} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000210
211/*
212 * Methods of combining two clusters
213 */
214#define CLUSTER_REPLACE 1 /* replace first list with second */
215#define CLUSTER_ADD 2 /* add second list to first */
216#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
217
Bram Moolenaar217ad922005-03-20 22:37:15 +0000218#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000219
220/*
221 * Syntax group IDs have different types:
222 * 0 - 9999 normal syntax groups
223 * 10000 - 14999 ALLBUT indicator (current_syn_inc_tag added)
224 * 15000 - 19999 TOP indicator (current_syn_inc_tag added)
225 * 20000 - 24999 CONTAINED indicator (current_syn_inc_tag added)
226 * >= 25000 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
227 */
228#define SYNID_ALLBUT 10000 /* syntax group ID for contains=ALLBUT */
229#define SYNID_TOP 15000 /* syntax group ID for contains=TOP */
230#define SYNID_CONTAINED 20000 /* syntax group ID for contains=CONTAINED */
231#define SYNID_CLUSTER 25000 /* first syntax group ID for clusters */
232
233/*
234 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
235 * expand_filename(). Most of the other syntax commands don't need it, so
236 * instead of passing it to them, we stow it here.
237 */
238static char_u **syn_cmdlinep;
239
240/*
241 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200242 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000243 * rules in each ":syn include"'d file.
244 */
245static int current_syn_inc_tag = 0;
246static int running_syn_inc_tag = 0;
247
248/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000249 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
250 * This avoids adding a pointer to the hashtable item.
251 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
252 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
253 * HI2KE() converts a hashitem pointer to a var pointer.
254 */
255static keyentry_T dumkey;
256#define KE2HIKEY(kp) ((kp)->keyword)
257#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
258#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
259
260/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000261 * To reduce the time spent in keepend(), remember at which level in the state
262 * stack the first item with "keepend" is present. When "-1", there is no
263 * "keepend" on the stack.
264 */
265static int keepend_level = -1;
266
267/*
268 * For the current state we need to remember more than just the idx.
269 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
270 * (The end positions have the column number of the next char)
271 */
272typedef struct state_item
273{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000274 int si_idx; /* index of syntax pattern or
275 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000276 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000277 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000278 int si_m_lnum; /* lnum of the match */
279 int si_m_startcol; /* starting column of the match */
280 lpos_T si_m_endpos; /* just after end posn of the match */
281 lpos_T si_h_startpos; /* start position of the highlighting */
282 lpos_T si_h_endpos; /* end position of the highlighting */
283 lpos_T si_eoe_pos; /* end position of end pattern */
284 int si_end_idx; /* group ID for end pattern or zero */
285 int si_ends; /* if match ends before si_m_endpos */
286 int si_attr; /* attributes in this state */
287 long si_flags; /* HL_HAS_EOL flag in this state, and
288 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200289#ifdef FEAT_CONCEAL
290 int si_char; /* substitution character for conceal */
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200291 int si_seqnr; /* sequence number */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200292#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000293 short *si_cont_list; /* list of contained groups */
294 short *si_next_list; /* nextgroup IDs after this item ends */
295 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
296 * pattern */
297} stateitem_T;
298
299#define KEYWORD_IDX -1 /* value of si_idx for keywords */
300#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
301 but contained groups */
302
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200303#ifdef FEAT_CONCEAL
304static int next_seqnr = 0; /* value to use for si_seqnr */
305#endif
306
Bram Moolenaar071d4272004-06-13 20:20:40 +0000307/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000308 * Struct to reduce the number of arguments to get_syn_options(), it's used
309 * very often.
310 */
311typedef struct
312{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000313 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000314 int keyword; /* TRUE for ":syn keyword" */
315 int *sync_idx; /* syntax item for "grouphere" argument, NULL
316 if not allowed */
317 char has_cont_list; /* TRUE if "cont_list" can be used */
318 short *cont_list; /* group IDs for "contains" argument */
319 short *cont_in_list; /* group IDs for "containedin" argument */
320 short *next_list; /* group IDs for "nextgroup" argument */
321} syn_opt_arg_T;
322
323/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000324 * The next possible match in the current line for any pattern is remembered,
325 * to avoid having to try for a match in each column.
326 * If next_match_idx == -1, not tried (in this line) yet.
327 * If next_match_col == MAXCOL, no match found in this line.
328 * (All end positions have the column of the char after the end)
329 */
330static int next_match_col; /* column for start of next match */
331static lpos_T next_match_m_endpos; /* position for end of next match */
332static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
333static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
334static int next_match_idx; /* index of matched item */
335static long next_match_flags; /* flags for next match */
336static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
337static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
338static int next_match_end_idx; /* ID of group for end pattn or zero */
339static reg_extmatch_T *next_match_extmatch = NULL;
340
341/*
342 * A state stack is an array of integers or stateitem_T, stored in a
343 * garray_T. A state stack is invalid if it's itemsize entry is zero.
344 */
345#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
346#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
347
348/*
349 * The current state (within the line) of the recognition engine.
350 * When current_state.ga_itemsize is 0 the current state is invalid.
351 */
352static win_T *syn_win; /* current window for highlighting */
353static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200354static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000355static linenr_T current_lnum = 0; /* lnum of current state */
356static colnr_T current_col = 0; /* column of current state */
357static int current_state_stored = 0; /* TRUE if stored current state
358 * after setting current_finished */
359static int current_finished = 0; /* current line has been finished */
360static garray_T current_state /* current stack of state_items */
361 = {0, 0, 0, 0, NULL};
362static short *current_next_list = NULL; /* when non-zero, nextgroup list */
363static int current_next_flags = 0; /* flags for current_next_list */
364static int current_line_id = 0; /* unique number for current line */
365
366#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
367
368static void syn_sync __ARGS((win_T *wp, linenr_T lnum, synstate_T *last_valid));
369static int syn_match_linecont __ARGS((linenr_T lnum));
370static void syn_start_line __ARGS((void));
371static void syn_update_ends __ARGS((int startofline));
372static void syn_stack_alloc __ARGS((void));
373static int syn_stack_cleanup __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200374static void syn_stack_free_entry __ARGS((synblock_T *block, synstate_T *p));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000375static synstate_T *syn_stack_find_entry __ARGS((linenr_T lnum));
Bram Moolenaardbe31752008-01-13 16:40:19 +0000376static synstate_T *store_current_state __ARGS((void));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000377static void load_current_state __ARGS((synstate_T *from));
378static void invalidate_current_state __ARGS((void));
379static int syn_stack_equal __ARGS((synstate_T *sp));
380static void validate_current_state __ARGS((void));
381static int syn_finish_line __ARGS((int syncing));
Bram Moolenaar56cefaf2008-01-12 15:47:10 +0000382static int syn_current_attr __ARGS((int syncing, int displaying, int *can_spell, int keep_state));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000383static int did_match_already __ARGS((int idx, garray_T *gap));
384static stateitem_T *push_next_match __ARGS((stateitem_T *cur_si));
385static void check_state_ends __ARGS((void));
386static void update_si_attr __ARGS((int idx));
387static void check_keepend __ARGS((void));
388static void update_si_end __ARGS((stateitem_T *sip, int startcol, int force));
389static short *copy_id_list __ARGS((short *list));
390static int in_id_list __ARGS((stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained));
391static int push_current_state __ARGS((int idx));
392static void pop_current_state __ARGS((void));
393
Bram Moolenaar860cae12010-06-05 23:22:07 +0200394static void syn_stack_apply_changes_block __ARGS((synblock_T *block, buf_T *buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000395static 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));
396static void clear_syn_state __ARGS((synstate_T *p));
397static void clear_current_state __ARGS((void));
398
399static void limit_pos __ARGS((lpos_T *pos, lpos_T *limit));
400static void limit_pos_zero __ARGS((lpos_T *pos, lpos_T *limit));
401static void syn_add_end_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
402static void syn_add_start_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
403static char_u *syn_getcurline __ARGS((void));
404static int syn_regexec __ARGS((regmmatch_T *rmp, linenr_T lnum, colnr_T col));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200405static 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 +0000406static void syn_cmd_case __ARGS((exarg_T *eap, int syncing));
Bram Moolenaarce0842a2005-07-18 21:58:11 +0000407static void syn_cmd_spell __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000408static void syntax_sync_clear __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200409static void syn_remove_pattern __ARGS((synblock_T *block, int idx));
410static void syn_clear_pattern __ARGS((synblock_T *block, int i));
411static void syn_clear_cluster __ARGS((synblock_T *block, int i));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000412static void syn_cmd_clear __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200413static void syn_cmd_conceal __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000414static void syn_clear_one __ARGS((int id, int syncing));
415static void syn_cmd_on __ARGS((exarg_T *eap, int syncing));
416static void syn_cmd_enable __ARGS((exarg_T *eap, int syncing));
417static void syn_cmd_reset __ARGS((exarg_T *eap, int syncing));
418static void syn_cmd_manual __ARGS((exarg_T *eap, int syncing));
419static void syn_cmd_off __ARGS((exarg_T *eap, int syncing));
420static void syn_cmd_onoff __ARGS((exarg_T *eap, char *name));
421static void syn_cmd_list __ARGS((exarg_T *eap, int syncing));
422static void syn_lines_msg __ARGS((void));
423static void syn_match_msg __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200424static void syn_stack_free_block __ARGS((synblock_T *block));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000425static void syn_list_one __ARGS((int id, int syncing, int link_only));
426static void syn_list_cluster __ARGS((int id));
427static void put_id_list __ARGS((char_u *name, short *list, int attr));
428static void put_pattern __ARGS((char *s, int c, synpat_T *spp, int attr));
Bram Moolenaardad6b692005-01-25 22:14:34 +0000429static int syn_list_keywords __ARGS((int id, hashtab_T *ht, int did_header, int attr));
430static void syn_clear_keyword __ARGS((int id, hashtab_T *ht));
431static void clear_keywtab __ARGS((hashtab_T *ht));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200432static 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 +0000433static char_u *get_group_name __ARGS((char_u *arg, char_u **name_end));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200434static char_u *get_syn_options __ARGS((char_u *arg, syn_opt_arg_T *opt, int *conceal_char));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000435static void syn_cmd_include __ARGS((exarg_T *eap, int syncing));
436static void syn_cmd_keyword __ARGS((exarg_T *eap, int syncing));
437static void syn_cmd_match __ARGS((exarg_T *eap, int syncing));
438static void syn_cmd_region __ARGS((exarg_T *eap, int syncing));
439#ifdef __BORLANDC__
440static int _RTLENTRYF syn_compare_stub __ARGS((const void *v1, const void *v2));
441#else
442static int syn_compare_stub __ARGS((const void *v1, const void *v2));
443#endif
444static void syn_cmd_cluster __ARGS((exarg_T *eap, int syncing));
445static int syn_scl_name2id __ARGS((char_u *name));
446static int syn_scl_namen2id __ARGS((char_u *linep, int len));
447static int syn_check_cluster __ARGS((char_u *pp, int len));
448static int syn_add_cluster __ARGS((char_u *name));
449static void init_syn_patterns __ARGS((void));
450static char_u *get_syn_pattern __ARGS((char_u *arg, synpat_T *ci));
451static void syn_cmd_sync __ARGS((exarg_T *eap, int syncing));
452static int get_id_list __ARGS((char_u **arg, int keylen, short **list));
453static void syn_combine_list __ARGS((short **clstr1, short **clstr2, int list_op));
454static void syn_incl_toplevel __ARGS((int id, int *flagsp));
455
456/*
457 * Start the syntax recognition for a line. This function is normally called
458 * from the screen updating, once for each displayed line.
459 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
460 * it. Careful: curbuf and curwin are likely to point to another buffer and
461 * window.
462 */
463 void
464syntax_start(wp, lnum)
465 win_T *wp;
466 linenr_T lnum;
467{
468 synstate_T *p;
469 synstate_T *last_valid = NULL;
470 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000471 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000472 linenr_T parsed_lnum;
473 linenr_T first_stored;
474 int dist;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000475 static int changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000476
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200477#ifdef FEAT_CONCEAL
478 current_sub_char = NUL;
479#endif
480
Bram Moolenaar071d4272004-06-13 20:20:40 +0000481 /*
482 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000483 * Also do this when a change was made, the current state may be invalid
484 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000485 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200486 if (syn_block != wp->w_s || changedtick != syn_buf->b_changedtick)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000487 {
488 invalidate_current_state();
489 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200490 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000491 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000492 changedtick = syn_buf->b_changedtick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000493 syn_win = wp;
494
495 /*
496 * Allocate syntax stack when needed.
497 */
498 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200499 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000500 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200501 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000502
503 /*
504 * If the state of the end of the previous line is useful, store it.
505 */
506 if (VALID_STATE(&current_state)
507 && current_lnum < lnum
508 && current_lnum < syn_buf->b_ml.ml_line_count)
509 {
510 (void)syn_finish_line(FALSE);
511 if (!current_state_stored)
512 {
513 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000514 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000515 }
516
517 /*
518 * If the current_lnum is now the same as "lnum", keep the current
519 * state (this happens very often!). Otherwise invalidate
520 * current_state and figure it out below.
521 */
522 if (current_lnum != lnum)
523 invalidate_current_state();
524 }
525 else
526 invalidate_current_state();
527
528 /*
529 * Try to synchronize from a saved state in b_sst_array[].
530 * Only do this if lnum is not before and not to far beyond a saved state.
531 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200532 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000533 {
534 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200535 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000536 {
537 if (p->sst_lnum > lnum)
538 break;
539 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
540 {
541 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200542 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000543 last_min_valid = p;
544 }
545 }
546 if (last_min_valid != NULL)
547 load_current_state(last_min_valid);
548 }
549
550 /*
551 * If "lnum" is before or far beyond a line with a saved state, need to
552 * re-synchronize.
553 */
554 if (INVALID_STATE(&current_state))
555 {
556 syn_sync(wp, lnum, last_valid);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200557 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000558 }
559 else
560 first_stored = current_lnum;
561
562 /*
563 * Advance from the sync point or saved state until the current line.
564 * Save some entries for syncing with later on.
565 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200566 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000567 dist = 999999;
568 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200569 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000570 while (current_lnum < lnum)
571 {
572 syn_start_line();
573 (void)syn_finish_line(FALSE);
574 ++current_lnum;
575
576 /* If we parsed at least "minlines" lines or started at a valid
577 * state, the current state is considered valid. */
578 if (current_lnum >= first_stored)
579 {
580 /* Check if the saved state entry is for the current line and is
581 * equal to the current state. If so, then validate all saved
582 * states that depended on a change before the parsed line. */
583 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000584 prev = syn_stack_find_entry(current_lnum - 1);
585 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200586 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000587 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000588 sp = prev;
589 while (sp != NULL && sp->sst_lnum < current_lnum)
590 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000591 if (sp != NULL
592 && sp->sst_lnum == current_lnum
593 && syn_stack_equal(sp))
594 {
595 parsed_lnum = current_lnum;
596 prev = sp;
597 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
598 {
599 if (sp->sst_lnum <= lnum)
600 /* valid state before desired line, use this one */
601 prev = sp;
602 else if (sp->sst_change_lnum == 0)
603 /* past saved states depending on change, break here. */
604 break;
605 sp->sst_change_lnum = 0;
606 sp = sp->sst_next;
607 }
608 load_current_state(prev);
609 }
610 /* Store the state at this line when it's the first one, the line
611 * where we start parsing, or some distance from the previously
612 * saved state. But only when parsed at least 'minlines'. */
613 else if (prev == NULL
614 || current_lnum == lnum
615 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000616 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000617 }
618
619 /* This can take a long time: break when CTRL-C pressed. The current
620 * state will be wrong then. */
621 line_breakcheck();
622 if (got_int)
623 {
624 current_lnum = lnum;
625 break;
626 }
627 }
628
629 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000630}
631
632/*
633 * We cannot simply discard growarrays full of state_items or buf_states; we
634 * have to manually release their extmatch pointers first.
635 */
636 static void
637clear_syn_state(p)
638 synstate_T *p;
639{
640 int i;
641 garray_T *gap;
642
643 if (p->sst_stacksize > SST_FIX_STATES)
644 {
645 gap = &(p->sst_union.sst_ga);
646 for (i = 0; i < gap->ga_len; i++)
647 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
648 ga_clear(gap);
649 }
650 else
651 {
652 for (i = 0; i < p->sst_stacksize; i++)
653 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
654 }
655}
656
657/*
658 * Cleanup the current_state stack.
659 */
660 static void
661clear_current_state()
662{
663 int i;
664 stateitem_T *sip;
665
666 sip = (stateitem_T *)(current_state.ga_data);
667 for (i = 0; i < current_state.ga_len; i++)
668 unref_extmatch(sip[i].si_extmatch);
669 ga_clear(&current_state);
670}
671
672/*
673 * Try to find a synchronisation point for line "lnum".
674 *
675 * This sets current_lnum and the current state. One of three methods is
676 * used:
677 * 1. Search backwards for the end of a C-comment.
678 * 2. Search backwards for given sync patterns.
679 * 3. Simply start on a given number of lines above "lnum".
680 */
681 static void
682syn_sync(wp, start_lnum, last_valid)
683 win_T *wp;
684 linenr_T start_lnum;
685 synstate_T *last_valid;
686{
687 buf_T *curbuf_save;
688 win_T *curwin_save;
689 pos_T cursor_save;
690 int idx;
691 linenr_T lnum;
692 linenr_T end_lnum;
693 linenr_T break_lnum;
694 int had_sync_point;
695 stateitem_T *cur_si;
696 synpat_T *spp;
697 char_u *line;
698 int found_flags = 0;
699 int found_match_idx = 0;
700 linenr_T found_current_lnum = 0;
701 int found_current_col= 0;
702 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000703 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000704
705 /*
706 * Clear any current state that might be hanging around.
707 */
708 invalidate_current_state();
709
710 /*
711 * Start at least "minlines" back. Default starting point for parsing is
712 * there.
713 * Start further back, to avoid that scrolling backwards will result in
714 * resyncing for every line. Now it resyncs only one out of N lines,
715 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
716 * Watch out for overflow when minlines is MAXLNUM.
717 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200718 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000719 start_lnum = 1;
720 else
721 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200722 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000723 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200724 else if (syn_block->b_syn_sync_minlines < 10)
725 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000726 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200727 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
728 if (syn_block->b_syn_sync_maxlines != 0
729 && lnum > syn_block->b_syn_sync_maxlines)
730 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000731 if (lnum >= start_lnum)
732 start_lnum = 1;
733 else
734 start_lnum -= lnum;
735 }
736 current_lnum = start_lnum;
737
738 /*
739 * 1. Search backwards for the end of a C-style comment.
740 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200741 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000742 {
743 /* Need to make syn_buf the current buffer for a moment, to be able to
744 * use find_start_comment(). */
745 curwin_save = curwin;
746 curwin = wp;
747 curbuf_save = curbuf;
748 curbuf = syn_buf;
749
750 /*
751 * Skip lines that end in a backslash.
752 */
753 for ( ; start_lnum > 1; --start_lnum)
754 {
755 line = ml_get(start_lnum - 1);
756 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
757 break;
758 }
759 current_lnum = start_lnum;
760
761 /* set cursor to start of search */
762 cursor_save = wp->w_cursor;
763 wp->w_cursor.lnum = start_lnum;
764 wp->w_cursor.col = 0;
765
766 /*
767 * If the line is inside a comment, need to find the syntax item that
768 * defines the comment.
769 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
770 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200771 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000772 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200773 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
774 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
775 == syn_block->b_syn_sync_id
776 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000777 {
778 validate_current_state();
779 if (push_current_state(idx) == OK)
780 update_si_attr(current_state.ga_len - 1);
781 break;
782 }
783 }
784
785 /* restore cursor and buffer */
786 wp->w_cursor = cursor_save;
787 curwin = curwin_save;
788 curbuf = curbuf_save;
789 }
790
791 /*
792 * 2. Search backwards for given sync patterns.
793 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200794 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000795 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200796 if (syn_block->b_syn_sync_maxlines != 0
797 && start_lnum > syn_block->b_syn_sync_maxlines)
798 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000799 else
800 break_lnum = 0;
801
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000802 found_m_endpos.lnum = 0;
803 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000804 end_lnum = start_lnum;
805 lnum = start_lnum;
806 while (--lnum > break_lnum)
807 {
808 /* This can take a long time: break when CTRL-C pressed. */
809 line_breakcheck();
810 if (got_int)
811 {
812 invalidate_current_state();
813 current_lnum = start_lnum;
814 break;
815 }
816
817 /* Check if we have run into a valid saved state stack now. */
818 if (last_valid != NULL && lnum == last_valid->sst_lnum)
819 {
820 load_current_state(last_valid);
821 break;
822 }
823
824 /*
825 * Check if the previous line has the line-continuation pattern.
826 */
827 if (lnum > 1 && syn_match_linecont(lnum - 1))
828 continue;
829
830 /*
831 * Start with nothing on the state stack
832 */
833 validate_current_state();
834
835 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
836 {
837 syn_start_line();
838 for (;;)
839 {
840 had_sync_point = syn_finish_line(TRUE);
841 /*
842 * When a sync point has been found, remember where, and
843 * continue to look for another one, further on in the line.
844 */
845 if (had_sync_point && current_state.ga_len)
846 {
847 cur_si = &CUR_STATE(current_state.ga_len - 1);
848 if (cur_si->si_m_endpos.lnum > start_lnum)
849 {
850 /* ignore match that goes to after where started */
851 current_lnum = end_lnum;
852 break;
853 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000854 if (cur_si->si_idx < 0)
855 {
856 /* Cannot happen? */
857 found_flags = 0;
858 found_match_idx = KEYWORD_IDX;
859 }
860 else
861 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200862 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000863 found_flags = spp->sp_flags;
864 found_match_idx = spp->sp_sync_idx;
865 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000866 found_current_lnum = current_lnum;
867 found_current_col = current_col;
868 found_m_endpos = cur_si->si_m_endpos;
869 /*
870 * Continue after the match (be aware of a zero-length
871 * match).
872 */
873 if (found_m_endpos.lnum > current_lnum)
874 {
875 current_lnum = found_m_endpos.lnum;
876 current_col = found_m_endpos.col;
877 if (current_lnum >= end_lnum)
878 break;
879 }
880 else if (found_m_endpos.col > current_col)
881 current_col = found_m_endpos.col;
882 else
883 ++current_col;
884
885 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000886 * an item that ends here, need to do that now. Be
887 * careful not to go past the NUL. */
888 prev_current_col = current_col;
889 if (syn_getcurline()[current_col] != NUL)
890 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000891 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000892 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000893 }
894 else
895 break;
896 }
897 }
898
899 /*
900 * If a sync point was encountered, break here.
901 */
902 if (found_flags)
903 {
904 /*
905 * Put the item that was specified by the sync point on the
906 * state stack. If there was no item specified, make the
907 * state stack empty.
908 */
909 clear_current_state();
910 if (found_match_idx >= 0
911 && push_current_state(found_match_idx) == OK)
912 update_si_attr(current_state.ga_len - 1);
913
914 /*
915 * When using "grouphere", continue from the sync point
916 * match, until the end of the line. Parsing starts at
917 * the next line.
918 * For "groupthere" the parsing starts at start_lnum.
919 */
920 if (found_flags & HL_SYNC_HERE)
921 {
922 if (current_state.ga_len)
923 {
924 cur_si = &CUR_STATE(current_state.ga_len - 1);
925 cur_si->si_h_startpos.lnum = found_current_lnum;
926 cur_si->si_h_startpos.col = found_current_col;
927 update_si_end(cur_si, (int)current_col, TRUE);
928 check_keepend();
929 }
930 current_col = found_m_endpos.col;
931 current_lnum = found_m_endpos.lnum;
932 (void)syn_finish_line(FALSE);
933 ++current_lnum;
934 }
935 else
936 current_lnum = start_lnum;
937
938 break;
939 }
940
941 end_lnum = lnum;
942 invalidate_current_state();
943 }
944
945 /* Ran into start of the file or exceeded maximum number of lines */
946 if (lnum <= break_lnum)
947 {
948 invalidate_current_state();
949 current_lnum = break_lnum + 1;
950 }
951 }
952
953 validate_current_state();
954}
955
956/*
957 * Return TRUE if the line-continuation pattern matches in line "lnum".
958 */
959 static int
960syn_match_linecont(lnum)
961 linenr_T lnum;
962{
963 regmmatch_T regmatch;
964
Bram Moolenaar860cae12010-06-05 23:22:07 +0200965 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000966 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200967 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
968 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000969 return syn_regexec(&regmatch, lnum, (colnr_T)0);
970 }
971 return FALSE;
972}
973
974/*
975 * Prepare the current state for the start of a line.
976 */
977 static void
978syn_start_line()
979{
980 current_finished = FALSE;
981 current_col = 0;
982
983 /*
984 * Need to update the end of a start/skip/end that continues from the
985 * previous line and regions that have "keepend".
986 */
987 if (current_state.ga_len > 0)
988 syn_update_ends(TRUE);
989
990 next_match_idx = -1;
991 ++current_line_id;
992}
993
994/*
995 * Check for items in the stack that need their end updated.
996 * When "startofline" is TRUE the last item is always updated.
997 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
998 */
999 static void
1000syn_update_ends(startofline)
1001 int startofline;
1002{
1003 stateitem_T *cur_si;
1004 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001005 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001006
1007 if (startofline)
1008 {
1009 /* Check for a match carried over from a previous line with a
1010 * contained region. The match ends as soon as the region ends. */
1011 for (i = 0; i < current_state.ga_len; ++i)
1012 {
1013 cur_si = &CUR_STATE(i);
1014 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001015 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001016 == SPTYPE_MATCH
1017 && cur_si->si_m_endpos.lnum < current_lnum)
1018 {
1019 cur_si->si_flags |= HL_MATCHCONT;
1020 cur_si->si_m_endpos.lnum = 0;
1021 cur_si->si_m_endpos.col = 0;
1022 cur_si->si_h_endpos = cur_si->si_m_endpos;
1023 cur_si->si_ends = TRUE;
1024 }
1025 }
1026 }
1027
1028 /*
1029 * Need to update the end of a start/skip/end that continues from the
1030 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001031 * influence contained items. If we've just removed "extend"
1032 * (startofline == 0) then we should update ends of normal regions
1033 * contained inside "keepend" because "extend" could have extended
1034 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001035 * Then check for items ending in column 0.
1036 */
1037 i = current_state.ga_len - 1;
1038 if (keepend_level >= 0)
1039 for ( ; i > keepend_level; --i)
1040 if (CUR_STATE(i).si_flags & HL_EXTEND)
1041 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001042
1043 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001044 for ( ; i < current_state.ga_len; ++i)
1045 {
1046 cur_si = &CUR_STATE(i);
1047 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001048 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001049 || (i == current_state.ga_len - 1 && startofline))
1050 {
1051 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1052 cur_si->si_h_startpos.lnum = current_lnum;
1053
1054 if (!(cur_si->si_flags & HL_MATCHCONT))
1055 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001056
1057 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1058 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001059 }
1060 }
1061 check_keepend();
1062 check_state_ends();
1063}
1064
1065/****************************************
1066 * Handling of the state stack cache.
1067 */
1068
1069/*
1070 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1071 *
1072 * To speed up syntax highlighting, the state stack for the start of some
1073 * lines is cached. These entries can be used to start parsing at that point.
1074 *
1075 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1076 * valid entries. b_sst_first points to the first one, then follow sst_next.
1077 * The entries are sorted on line number. The first entry is often for line 2
1078 * (line 1 always starts with an empty stack).
1079 * There is also a list for free entries. This construction is used to avoid
1080 * having to allocate and free memory blocks too often.
1081 *
1082 * When making changes to the buffer, this is logged in b_mod_*. When calling
1083 * update_screen() to update the display, it will call
1084 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1085 * entries. The entries which are inside the changed area are removed,
1086 * because they must be recomputed. Entries below the changed have their line
1087 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1088 * set to indicate that a check must be made if the changed lines would change
1089 * the cached entry.
1090 *
1091 * When later displaying lines, an entry is stored for each line. Displayed
1092 * lines are likely to be displayed again, in which case the state at the
1093 * start of the line is needed.
1094 * For not displayed lines, an entry is stored for every so many lines. These
1095 * entries will be used e.g., when scrolling backwards. The distance between
1096 * entries depends on the number of lines in the buffer. For small buffers
1097 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1098 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1099 */
1100
Bram Moolenaar860cae12010-06-05 23:22:07 +02001101 static void
1102syn_stack_free_block(block)
1103 synblock_T *block;
1104{
1105 synstate_T *p;
1106
1107 if (block->b_sst_array != NULL)
1108 {
1109 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1110 clear_syn_state(p);
1111 vim_free(block->b_sst_array);
1112 block->b_sst_array = NULL;
1113 block->b_sst_len = 0;
1114 }
1115}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001116/*
1117 * Free b_sst_array[] for buffer "buf".
1118 * Used when syntax items changed to force resyncing everywhere.
1119 */
1120 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001121syn_stack_free_all(block)
1122 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001123{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001124 win_T *wp;
1125
Bram Moolenaar860cae12010-06-05 23:22:07 +02001126 syn_stack_free_block(block);
1127
1128
Bram Moolenaar071d4272004-06-13 20:20:40 +00001129#ifdef FEAT_FOLDING
1130 /* When using "syntax" fold method, must update all folds. */
1131 FOR_ALL_WINDOWS(wp)
1132 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001133 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001134 foldUpdateAll(wp);
1135 }
1136#endif
1137}
1138
1139/*
1140 * Allocate the syntax state stack for syn_buf when needed.
1141 * If the number of entries in b_sst_array[] is much too big or a bit too
1142 * small, reallocate it.
1143 * Also used to allocate b_sst_array[] for the first time.
1144 */
1145 static void
1146syn_stack_alloc()
1147{
1148 long len;
1149 synstate_T *to, *from;
1150 synstate_T *sstp;
1151
1152 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1153 if (len < SST_MIN_ENTRIES)
1154 len = SST_MIN_ENTRIES;
1155 else if (len > SST_MAX_ENTRIES)
1156 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001157 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001158 {
1159 /* Allocate 50% too much, to avoid reallocating too often. */
1160 len = syn_buf->b_ml.ml_line_count;
1161 len = (len + len / 2) / SST_DIST + Rows * 2;
1162 if (len < SST_MIN_ENTRIES)
1163 len = SST_MIN_ENTRIES;
1164 else if (len > SST_MAX_ENTRIES)
1165 len = SST_MAX_ENTRIES;
1166
Bram Moolenaar860cae12010-06-05 23:22:07 +02001167 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001168 {
1169 /* When shrinking the array, cleanup the existing stack.
1170 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001171 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001172 && syn_stack_cleanup())
1173 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001174 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1175 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001176 }
1177
1178 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1179 if (sstp == NULL) /* out of memory! */
1180 return;
1181
1182 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001183 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001184 {
1185 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001186 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001187 from = from->sst_next)
1188 {
1189 ++to;
1190 *to = *from;
1191 to->sst_next = to + 1;
1192 }
1193 }
1194 if (to != sstp - 1)
1195 {
1196 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001197 syn_block->b_sst_first = sstp;
1198 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001199 }
1200 else
1201 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001202 syn_block->b_sst_first = NULL;
1203 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001204 }
1205
1206 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001207 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001208 while (++to < sstp + len)
1209 to->sst_next = to + 1;
1210 (sstp + len - 1)->sst_next = NULL;
1211
Bram Moolenaar860cae12010-06-05 23:22:07 +02001212 vim_free(syn_block->b_sst_array);
1213 syn_block->b_sst_array = sstp;
1214 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001215 }
1216}
1217
1218/*
1219 * Check for changes in a buffer to affect stored syntax states. Uses the
1220 * b_mod_* fields.
1221 * Called from update_screen(), before screen is being updated, once for each
1222 * displayed buffer.
1223 */
1224 void
1225syn_stack_apply_changes(buf)
1226 buf_T *buf;
1227{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001228 win_T *wp;
1229
1230 syn_stack_apply_changes_block(&buf->b_s, buf);
1231
1232 FOR_ALL_WINDOWS(wp)
1233 {
1234 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1235 syn_stack_apply_changes_block(wp->w_s, buf);
1236 }
1237}
1238
1239 static void
1240syn_stack_apply_changes_block(block, buf)
1241 synblock_T *block;
1242 buf_T *buf;
1243{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244 synstate_T *p, *prev, *np;
1245 linenr_T n;
1246
Bram Moolenaar860cae12010-06-05 23:22:07 +02001247 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001248 return;
1249
1250 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001251 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001252 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001253 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254 {
1255 n = p->sst_lnum + buf->b_mod_xlines;
1256 if (n <= buf->b_mod_bot)
1257 {
1258 /* this state is inside the changed area, remove it */
1259 np = p->sst_next;
1260 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001261 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001262 else
1263 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001264 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001265 p = np;
1266 continue;
1267 }
1268 /* This state is below the changed area. Remember the line
1269 * that needs to be parsed before this entry can be made valid
1270 * again. */
1271 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1272 {
1273 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1274 p->sst_change_lnum += buf->b_mod_xlines;
1275 else
1276 p->sst_change_lnum = buf->b_mod_top;
1277 }
1278 if (p->sst_change_lnum == 0
1279 || p->sst_change_lnum < buf->b_mod_bot)
1280 p->sst_change_lnum = buf->b_mod_bot;
1281
1282 p->sst_lnum = n;
1283 }
1284 prev = p;
1285 p = p->sst_next;
1286 }
1287}
1288
1289/*
1290 * Reduce the number of entries in the state stack for syn_buf.
1291 * Returns TRUE if at least one entry was freed.
1292 */
1293 static int
1294syn_stack_cleanup()
1295{
1296 synstate_T *p, *prev;
1297 disptick_T tick;
1298 int above;
1299 int dist;
1300 int retval = FALSE;
1301
Bram Moolenaar860cae12010-06-05 23:22:07 +02001302 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001303 return retval;
1304
1305 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001306 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001307 dist = 999999;
1308 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001309 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001310
1311 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001312 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001313 * be removed. Set "above" when the "tick" for the oldest entry is above
1314 * "b_sst_lasttick" (the display tick wraps around).
1315 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001316 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001317 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001318 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001319 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1320 {
1321 if (prev->sst_lnum + dist > p->sst_lnum)
1322 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001323 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324 {
1325 if (!above || p->sst_tick < tick)
1326 tick = p->sst_tick;
1327 above = TRUE;
1328 }
1329 else if (!above && p->sst_tick < tick)
1330 tick = p->sst_tick;
1331 }
1332 }
1333
1334 /*
1335 * Go through the list to make the entries for the oldest tick at an
1336 * interval of several lines.
1337 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001338 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001339 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1340 {
1341 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1342 {
1343 /* Move this entry from used list to free list */
1344 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001345 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001346 p = prev;
1347 retval = TRUE;
1348 }
1349 }
1350 return retval;
1351}
1352
1353/*
1354 * Free the allocated memory for a syn_state item.
1355 * Move the entry into the free list.
1356 */
1357 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001358syn_stack_free_entry(block, p)
1359 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001360 synstate_T *p;
1361{
1362 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001363 p->sst_next = block->b_sst_firstfree;
1364 block->b_sst_firstfree = p;
1365 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001366}
1367
1368/*
1369 * Find an entry in the list of state stacks at or before "lnum".
1370 * Returns NULL when there is no entry or the first entry is after "lnum".
1371 */
1372 static synstate_T *
1373syn_stack_find_entry(lnum)
1374 linenr_T lnum;
1375{
1376 synstate_T *p, *prev;
1377
1378 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001379 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001380 {
1381 if (p->sst_lnum == lnum)
1382 return p;
1383 if (p->sst_lnum > lnum)
1384 break;
1385 }
1386 return prev;
1387}
1388
1389/*
1390 * Try saving the current state in b_sst_array[].
1391 * The current state must be valid for the start of the current_lnum line!
1392 */
1393 static synstate_T *
Bram Moolenaardbe31752008-01-13 16:40:19 +00001394store_current_state()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001395{
1396 int i;
1397 synstate_T *p;
1398 bufstate_T *bp;
1399 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001400 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001401
1402 /*
1403 * If the current state contains a start or end pattern that continues
1404 * from the previous line, we can't use it. Don't store it then.
1405 */
1406 for (i = current_state.ga_len - 1; i >= 0; --i)
1407 {
1408 cur_si = &CUR_STATE(i);
1409 if (cur_si->si_h_startpos.lnum >= current_lnum
1410 || cur_si->si_m_endpos.lnum >= current_lnum
1411 || cur_si->si_h_endpos.lnum >= current_lnum
1412 || (cur_si->si_end_idx
1413 && cur_si->si_eoe_pos.lnum >= current_lnum))
1414 break;
1415 }
1416 if (i >= 0)
1417 {
1418 if (sp != NULL)
1419 {
1420 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001421 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001422 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001423 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001424 else
1425 {
1426 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001427 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001428 if (p->sst_next == sp)
1429 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001430 if (p != NULL) /* just in case */
1431 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001432 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001433 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001434 sp = NULL;
1435 }
1436 }
1437 else if (sp == NULL || sp->sst_lnum != current_lnum)
1438 {
1439 /*
1440 * Add a new entry
1441 */
1442 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001443 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001444 {
1445 (void)syn_stack_cleanup();
1446 /* "sp" may have been moved to the freelist now */
1447 sp = syn_stack_find_entry(current_lnum);
1448 }
1449 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001450 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001451 sp = NULL;
1452 else
1453 {
1454 /* Take the first item from the free list and put it in the used
1455 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001456 p = syn_block->b_sst_firstfree;
1457 syn_block->b_sst_firstfree = p->sst_next;
1458 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001459 if (sp == NULL)
1460 {
1461 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001462 p->sst_next = syn_block->b_sst_first;
1463 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001464 }
1465 else
1466 {
1467 /* insert in list after *sp */
1468 p->sst_next = sp->sst_next;
1469 sp->sst_next = p;
1470 }
1471 sp = p;
1472 sp->sst_stacksize = 0;
1473 sp->sst_lnum = current_lnum;
1474 }
1475 }
1476 if (sp != NULL)
1477 {
1478 /* When overwriting an existing state stack, clear it first */
1479 clear_syn_state(sp);
1480 sp->sst_stacksize = current_state.ga_len;
1481 if (current_state.ga_len > SST_FIX_STATES)
1482 {
1483 /* Need to clear it, might be something remaining from when the
1484 * length was less than SST_FIX_STATES. */
1485 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1486 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1487 sp->sst_stacksize = 0;
1488 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001489 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001490 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1491 }
1492 else
1493 bp = sp->sst_union.sst_stack;
1494 for (i = 0; i < sp->sst_stacksize; ++i)
1495 {
1496 bp[i].bs_idx = CUR_STATE(i).si_idx;
1497 bp[i].bs_flags = CUR_STATE(i).si_flags;
1498 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1499 }
1500 sp->sst_next_flags = current_next_flags;
1501 sp->sst_next_list = current_next_list;
1502 sp->sst_tick = display_tick;
1503 sp->sst_change_lnum = 0;
1504 }
1505 current_state_stored = TRUE;
1506 return sp;
1507}
1508
1509/*
1510 * Copy a state stack from "from" in b_sst_array[] to current_state;
1511 */
1512 static void
1513load_current_state(from)
1514 synstate_T *from;
1515{
1516 int i;
1517 bufstate_T *bp;
1518
1519 clear_current_state();
1520 validate_current_state();
1521 keepend_level = -1;
1522 if (from->sst_stacksize
1523 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1524 {
1525 if (from->sst_stacksize > SST_FIX_STATES)
1526 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1527 else
1528 bp = from->sst_union.sst_stack;
1529 for (i = 0; i < from->sst_stacksize; ++i)
1530 {
1531 CUR_STATE(i).si_idx = bp[i].bs_idx;
1532 CUR_STATE(i).si_flags = bp[i].bs_flags;
1533 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1534 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1535 keepend_level = i;
1536 CUR_STATE(i).si_ends = FALSE;
1537 CUR_STATE(i).si_m_lnum = 0;
1538 if (CUR_STATE(i).si_idx >= 0)
1539 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001540 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001541 else
1542 CUR_STATE(i).si_next_list = NULL;
1543 update_si_attr(i);
1544 }
1545 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001546 }
1547 current_next_list = from->sst_next_list;
1548 current_next_flags = from->sst_next_flags;
1549 current_lnum = from->sst_lnum;
1550}
1551
1552/*
1553 * Compare saved state stack "*sp" with the current state.
1554 * Return TRUE when they are equal.
1555 */
1556 static int
1557syn_stack_equal(sp)
1558 synstate_T *sp;
1559{
1560 int i, j;
1561 bufstate_T *bp;
1562 reg_extmatch_T *six, *bsx;
1563
1564 /* First a quick check if the stacks have the same size end nextlist. */
1565 if (sp->sst_stacksize == current_state.ga_len
1566 && sp->sst_next_list == current_next_list)
1567 {
1568 /* Need to compare all states on both stacks. */
1569 if (sp->sst_stacksize > SST_FIX_STATES)
1570 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1571 else
1572 bp = sp->sst_union.sst_stack;
1573
1574 for (i = current_state.ga_len; --i >= 0; )
1575 {
1576 /* If the item has another index the state is different. */
1577 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1578 break;
1579 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1580 {
1581 /* When the extmatch pointers are different, the strings in
1582 * them can still be the same. Check if the extmatch
1583 * references are equal. */
1584 bsx = bp[i].bs_extmatch;
1585 six = CUR_STATE(i).si_extmatch;
1586 /* If one of the extmatch pointers is NULL the states are
1587 * different. */
1588 if (bsx == NULL || six == NULL)
1589 break;
1590 for (j = 0; j < NSUBEXP; ++j)
1591 {
1592 /* Check each referenced match string. They must all be
1593 * equal. */
1594 if (bsx->matches[j] != six->matches[j])
1595 {
1596 /* If the pointer is different it can still be the
1597 * same text. Compare the strings, ignore case when
1598 * the start item has the sp_ic flag set. */
1599 if (bsx->matches[j] == NULL
1600 || six->matches[j] == NULL)
1601 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001602 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001603 ? MB_STRICMP(bsx->matches[j],
1604 six->matches[j]) != 0
1605 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1606 break;
1607 }
1608 }
1609 if (j != NSUBEXP)
1610 break;
1611 }
1612 }
1613 if (i < 0)
1614 return TRUE;
1615 }
1616 return FALSE;
1617}
1618
1619/*
1620 * We stop parsing syntax above line "lnum". If the stored state at or below
1621 * this line depended on a change before it, it now depends on the line below
1622 * the last parsed line.
1623 * The window looks like this:
1624 * line which changed
1625 * displayed line
1626 * displayed line
1627 * lnum -> line below window
1628 */
1629 void
1630syntax_end_parsing(lnum)
1631 linenr_T lnum;
1632{
1633 synstate_T *sp;
1634
1635 sp = syn_stack_find_entry(lnum);
1636 if (sp != NULL && sp->sst_lnum < lnum)
1637 sp = sp->sst_next;
1638
1639 if (sp != NULL && sp->sst_change_lnum != 0)
1640 sp->sst_change_lnum = lnum;
1641}
1642
1643/*
1644 * End of handling of the state stack.
1645 ****************************************/
1646
1647 static void
1648invalidate_current_state()
1649{
1650 clear_current_state();
1651 current_state.ga_itemsize = 0; /* mark current_state invalid */
1652 current_next_list = NULL;
1653 keepend_level = -1;
1654}
1655
1656 static void
1657validate_current_state()
1658{
1659 current_state.ga_itemsize = sizeof(stateitem_T);
1660 current_state.ga_growsize = 3;
1661}
1662
1663/*
1664 * Return TRUE if the syntax at start of lnum changed since last time.
1665 * This will only be called just after get_syntax_attr() for the previous
1666 * line, to check if the next line needs to be redrawn too.
1667 */
1668 int
1669syntax_check_changed(lnum)
1670 linenr_T lnum;
1671{
1672 int retval = TRUE;
1673 synstate_T *sp;
1674
Bram Moolenaar071d4272004-06-13 20:20:40 +00001675 /*
1676 * Check the state stack when:
1677 * - lnum is just below the previously syntaxed line.
1678 * - lnum is not before the lines with saved states.
1679 * - lnum is not past the lines with saved states.
1680 * - lnum is at or before the last changed line.
1681 */
1682 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1683 {
1684 sp = syn_stack_find_entry(lnum);
1685 if (sp != NULL && sp->sst_lnum == lnum)
1686 {
1687 /*
1688 * finish the previous line (needed when not all of the line was
1689 * drawn)
1690 */
1691 (void)syn_finish_line(FALSE);
1692
1693 /*
1694 * Compare the current state with the previously saved state of
1695 * the line.
1696 */
1697 if (syn_stack_equal(sp))
1698 retval = FALSE;
1699
1700 /*
1701 * Store the current state in b_sst_array[] for later use.
1702 */
1703 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001704 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001705 }
1706 }
1707
Bram Moolenaar071d4272004-06-13 20:20:40 +00001708 return retval;
1709}
1710
1711/*
1712 * Finish the current line.
1713 * This doesn't return any attributes, it only gets the state at the end of
1714 * the line. It can start anywhere in the line, as long as the current state
1715 * is valid.
1716 */
1717 static int
1718syn_finish_line(syncing)
1719 int syncing; /* called for syncing */
1720{
1721 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001722 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001723
1724 if (!current_finished)
1725 {
1726 while (!current_finished)
1727 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001728 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001729 /*
1730 * When syncing, and found some item, need to check the item.
1731 */
1732 if (syncing && current_state.ga_len)
1733 {
1734 /*
1735 * Check for match with sync item.
1736 */
1737 cur_si = &CUR_STATE(current_state.ga_len - 1);
1738 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001739 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001740 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1741 return TRUE;
1742
1743 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001744 * that ends here, need to do that now. Be careful not to go
1745 * past the NUL. */
1746 prev_current_col = current_col;
1747 if (syn_getcurline()[current_col] != NUL)
1748 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001749 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001750 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001751 }
1752 ++current_col;
1753 }
1754 }
1755 return FALSE;
1756}
1757
1758/*
1759 * Return highlight attributes for next character.
1760 * Must first call syntax_start() once for the line.
1761 * "col" is normally 0 for the first use in a line, and increments by one each
1762 * time. It's allowed to skip characters and to stop before the end of the
1763 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001764 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1765 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001766 */
1767 int
Bram Moolenaar27c735b2010-07-22 22:16:29 +02001768get_syntax_attr(col, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001769 colnr_T col;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001770 int *can_spell;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001771 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001772{
1773 int attr = 0;
1774
Bram Moolenaar349955a2007-08-14 21:07:36 +00001775 if (can_spell != NULL)
1776 /* Default: Only do spelling when there is no @Spell cluster or when
1777 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001778 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1779 ? (syn_block->b_spell_cluster_id == 0)
1780 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001781
Bram Moolenaar071d4272004-06-13 20:20:40 +00001782 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001783 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001784 return 0;
1785
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001786 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001787 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001788 {
1789 clear_current_state();
1790#ifdef FEAT_EVAL
1791 current_id = 0;
1792 current_trans_id = 0;
1793#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001794#ifdef FEAT_CONCEAL
1795 current_flags = 0;
1796#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001797 return 0;
1798 }
1799
Bram Moolenaar071d4272004-06-13 20:20:40 +00001800 /* Make sure current_state is valid */
1801 if (INVALID_STATE(&current_state))
1802 validate_current_state();
1803
1804 /*
1805 * Skip from the current column to "col", get the attributes for "col".
1806 */
1807 while (current_col <= col)
1808 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001809 attr = syn_current_attr(FALSE, TRUE, can_spell,
1810 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811 ++current_col;
1812 }
1813
Bram Moolenaar071d4272004-06-13 20:20:40 +00001814 return attr;
1815}
1816
1817/*
1818 * Get syntax attributes for current_lnum, current_col.
1819 */
1820 static int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001821syn_current_attr(syncing, displaying, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001822 int syncing; /* When 1: called for syncing */
1823 int displaying; /* result will be displayed */
Bram Moolenaar217ad922005-03-20 22:37:15 +00001824 int *can_spell; /* return: do spell checking */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001825 int keep_state; /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001826{
1827 int syn_id;
1828 lpos_T endpos; /* was: char_u *endp; */
1829 lpos_T hl_startpos; /* was: int hl_startcol; */
1830 lpos_T hl_endpos;
1831 lpos_T eos_pos; /* end-of-start match (start region) */
1832 lpos_T eoe_pos; /* end-of-end pattern */
1833 int end_idx; /* group ID for end pattern */
1834 int idx;
1835 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001836 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001837 int startcol;
1838 int endcol;
1839 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001840 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001841 short *next_list;
1842 int found_match; /* found usable match */
1843 static int try_next_column = FALSE; /* must try in next col */
1844 int do_keywords;
1845 regmmatch_T regmatch;
1846 lpos_T pos;
1847 int lc_col;
1848 reg_extmatch_T *cur_extmatch = NULL;
1849 char_u *line; /* current line. NOTE: becomes invalid after
1850 looking for a pattern match! */
1851
1852 /* variables for zero-width matches that have a "nextgroup" argument */
1853 int keep_next_list;
1854 int zero_width_next_list = FALSE;
1855 garray_T zero_width_next_ga;
1856
1857 /*
1858 * No character, no attributes! Past end of line?
1859 * Do try matching with an empty line (could be the start of a region).
1860 */
1861 line = syn_getcurline();
1862 if (line[current_col] == NUL && current_col != 0)
1863 {
1864 /*
1865 * If we found a match after the last column, use it.
1866 */
1867 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1868 && next_match_col != MAXCOL)
1869 (void)push_next_match(NULL);
1870
1871 current_finished = TRUE;
1872 current_state_stored = FALSE;
1873 return 0;
1874 }
1875
1876 /* if the current or next character is NUL, we will finish the line now */
1877 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1878 {
1879 current_finished = TRUE;
1880 current_state_stored = FALSE;
1881 }
1882
1883 /*
1884 * When in the previous column there was a match but it could not be used
1885 * (empty match or already matched in this column) need to try again in
1886 * the next column.
1887 */
1888 if (try_next_column)
1889 {
1890 next_match_idx = -1;
1891 try_next_column = FALSE;
1892 }
1893
1894 /* Only check for keywords when not syncing and there are some. */
1895 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001896 && (syn_block->b_keywtab.ht_used > 0
1897 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001898
1899 /* Init the list of zero-width matches with a nextlist. This is used to
1900 * avoid matching the same item in the same position twice. */
1901 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1902
1903 /*
1904 * Repeat matching keywords and patterns, to find contained items at the
1905 * same column. This stops when there are no extra matches at the current
1906 * column.
1907 */
1908 do
1909 {
1910 found_match = FALSE;
1911 keep_next_list = FALSE;
1912 syn_id = 0;
1913
1914 /*
1915 * 1. Check for a current state.
1916 * Only when there is no current state, or if the current state may
1917 * contain other things, we need to check for keywords and patterns.
1918 * Always need to check for contained items if some item has the
1919 * "containedin" argument (takes extra time!).
1920 */
1921 if (current_state.ga_len)
1922 cur_si = &CUR_STATE(current_state.ga_len - 1);
1923 else
1924 cur_si = NULL;
1925
Bram Moolenaar860cae12010-06-05 23:22:07 +02001926 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001927 || cur_si->si_cont_list != NULL)
1928 {
1929 /*
1930 * 2. Check for keywords, if on a keyword char after a non-keyword
1931 * char. Don't do this when syncing.
1932 */
1933 if (do_keywords)
1934 {
1935 line = syn_getcurline();
1936 if (vim_iswordc_buf(line + current_col, syn_buf)
1937 && (current_col == 0
1938 || !vim_iswordc_buf(line + current_col - 1
1939#ifdef FEAT_MBYTE
1940 - (has_mbyte
1941 ? (*mb_head_off)(line, line + current_col - 1)
1942 : 0)
1943#endif
1944 , syn_buf)))
1945 {
1946 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001947 &endcol, &flags, &next_list, cur_si,
1948 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001949 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001950 {
1951 if (push_current_state(KEYWORD_IDX) == OK)
1952 {
1953 cur_si = &CUR_STATE(current_state.ga_len - 1);
1954 cur_si->si_m_startcol = current_col;
1955 cur_si->si_h_startpos.lnum = current_lnum;
1956 cur_si->si_h_startpos.col = 0; /* starts right away */
1957 cur_si->si_m_endpos.lnum = current_lnum;
1958 cur_si->si_m_endpos.col = endcol;
1959 cur_si->si_h_endpos.lnum = current_lnum;
1960 cur_si->si_h_endpos.col = endcol;
1961 cur_si->si_ends = TRUE;
1962 cur_si->si_end_idx = 0;
1963 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001964#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02001965 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001966 cur_si->si_char = cchar;
1967 if (current_state.ga_len > 1)
1968 cur_si->si_flags |=
1969 CUR_STATE(current_state.ga_len - 2).si_flags
1970 & HL_CONCEAL;
1971#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001972 cur_si->si_id = syn_id;
1973 cur_si->si_trans_id = syn_id;
1974 if (flags & HL_TRANSP)
1975 {
1976 if (current_state.ga_len < 2)
1977 {
1978 cur_si->si_attr = 0;
1979 cur_si->si_trans_id = 0;
1980 }
1981 else
1982 {
1983 cur_si->si_attr = CUR_STATE(
1984 current_state.ga_len - 2).si_attr;
1985 cur_si->si_trans_id = CUR_STATE(
1986 current_state.ga_len - 2).si_trans_id;
1987 }
1988 }
1989 else
1990 cur_si->si_attr = syn_id2attr(syn_id);
1991 cur_si->si_cont_list = NULL;
1992 cur_si->si_next_list = next_list;
1993 check_keepend();
1994 }
1995 else
1996 vim_free(next_list);
1997 }
1998 }
1999 }
2000
2001 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002002 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002003 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002004 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002005 {
2006 /*
2007 * If we didn't check for a match yet, or we are past it, check
2008 * for any match with a pattern.
2009 */
2010 if (next_match_idx < 0 || next_match_col < (int)current_col)
2011 {
2012 /*
2013 * Check all relevant patterns for a match at this
2014 * position. This is complicated, because matching with a
2015 * pattern takes quite a bit of time, thus we want to
2016 * avoid doing it when it's not needed.
2017 */
2018 next_match_idx = 0; /* no match in this line yet */
2019 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002020 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002021 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002022 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002023 if ( spp->sp_syncing == syncing
2024 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2025 && (spp->sp_type == SPTYPE_MATCH
2026 || spp->sp_type == SPTYPE_START)
2027 && (current_next_list != NULL
2028 ? in_id_list(NULL, current_next_list,
2029 &spp->sp_syn, 0)
2030 : (cur_si == NULL
2031 ? !(spp->sp_flags & HL_CONTAINED)
2032 : in_id_list(cur_si,
2033 cur_si->si_cont_list, &spp->sp_syn,
2034 spp->sp_flags & HL_CONTAINED))))
2035 {
2036 /* If we already tried matching in this line, and
2037 * there isn't a match before next_match_col, skip
2038 * this item. */
2039 if (spp->sp_line_id == current_line_id
2040 && spp->sp_startcol >= next_match_col)
2041 continue;
2042 spp->sp_line_id = current_line_id;
2043
2044 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2045 if (lc_col < 0)
2046 lc_col = 0;
2047
2048 regmatch.rmm_ic = spp->sp_ic;
2049 regmatch.regprog = spp->sp_prog;
2050 if (!syn_regexec(&regmatch, current_lnum,
2051 (colnr_T)lc_col))
2052 {
2053 /* no match in this line, try another one */
2054 spp->sp_startcol = MAXCOL;
2055 continue;
2056 }
2057
2058 /*
2059 * Compute the first column of the match.
2060 */
2061 syn_add_start_off(&pos, &regmatch,
2062 spp, SPO_MS_OFF, -1);
2063 if (pos.lnum > current_lnum)
2064 {
2065 /* must have used end of match in a next line,
2066 * we can't handle that */
2067 spp->sp_startcol = MAXCOL;
2068 continue;
2069 }
2070 startcol = pos.col;
2071
2072 /* remember the next column where this pattern
2073 * matches in the current line */
2074 spp->sp_startcol = startcol;
2075
2076 /*
2077 * If a previously found match starts at a lower
2078 * column number, don't use this one.
2079 */
2080 if (startcol >= next_match_col)
2081 continue;
2082
2083 /*
2084 * If we matched this pattern at this position
2085 * before, skip it. Must retry in the next
2086 * column, because it may match from there.
2087 */
2088 if (did_match_already(idx, &zero_width_next_ga))
2089 {
2090 try_next_column = TRUE;
2091 continue;
2092 }
2093
2094 endpos.lnum = regmatch.endpos[0].lnum;
2095 endpos.col = regmatch.endpos[0].col;
2096
2097 /* Compute the highlight start. */
2098 syn_add_start_off(&hl_startpos, &regmatch,
2099 spp, SPO_HS_OFF, -1);
2100
2101 /* Compute the region start. */
2102 /* Default is to use the end of the match. */
2103 syn_add_end_off(&eos_pos, &regmatch,
2104 spp, SPO_RS_OFF, 0);
2105
2106 /*
2107 * Grab the external submatches before they get
2108 * overwritten. Reference count doesn't change.
2109 */
2110 unref_extmatch(cur_extmatch);
2111 cur_extmatch = re_extmatch_out;
2112 re_extmatch_out = NULL;
2113
2114 flags = 0;
2115 eoe_pos.lnum = 0; /* avoid warning */
2116 eoe_pos.col = 0;
2117 end_idx = 0;
2118 hl_endpos.lnum = 0;
2119
2120 /*
2121 * For a "oneline" the end must be found in the
2122 * same line too. Search for it after the end of
2123 * the match with the start pattern. Set the
2124 * resulting end positions at the same time.
2125 */
2126 if (spp->sp_type == SPTYPE_START
2127 && (spp->sp_flags & HL_ONELINE))
2128 {
2129 lpos_T startpos;
2130
2131 startpos = endpos;
2132 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2133 &flags, &eoe_pos, &end_idx, cur_extmatch);
2134 if (endpos.lnum == 0)
2135 continue; /* not found */
2136 }
2137
2138 /*
2139 * For a "match" the size must be > 0 after the
2140 * end offset needs has been added. Except when
2141 * syncing.
2142 */
2143 else if (spp->sp_type == SPTYPE_MATCH)
2144 {
2145 syn_add_end_off(&hl_endpos, &regmatch, spp,
2146 SPO_HE_OFF, 0);
2147 syn_add_end_off(&endpos, &regmatch, spp,
2148 SPO_ME_OFF, 0);
2149 if (endpos.lnum == current_lnum
2150 && (int)endpos.col + syncing < startcol)
2151 {
2152 /*
2153 * If an empty string is matched, may need
2154 * to try matching again at next column.
2155 */
2156 if (regmatch.startpos[0].col
2157 == regmatch.endpos[0].col)
2158 try_next_column = TRUE;
2159 continue;
2160 }
2161 }
2162
2163 /*
2164 * keep the best match so far in next_match_*
2165 */
2166 /* Highlighting must start after startpos and end
2167 * before endpos. */
2168 if (hl_startpos.lnum == current_lnum
2169 && (int)hl_startpos.col < startcol)
2170 hl_startpos.col = startcol;
2171 limit_pos_zero(&hl_endpos, &endpos);
2172
2173 next_match_idx = idx;
2174 next_match_col = startcol;
2175 next_match_m_endpos = endpos;
2176 next_match_h_endpos = hl_endpos;
2177 next_match_h_startpos = hl_startpos;
2178 next_match_flags = flags;
2179 next_match_eos_pos = eos_pos;
2180 next_match_eoe_pos = eoe_pos;
2181 next_match_end_idx = end_idx;
2182 unref_extmatch(next_match_extmatch);
2183 next_match_extmatch = cur_extmatch;
2184 cur_extmatch = NULL;
2185 }
2186 }
2187 }
2188
2189 /*
2190 * If we found a match at the current column, use it.
2191 */
2192 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2193 {
2194 synpat_T *lspp;
2195
2196 /* When a zero-width item matched which has a nextgroup,
2197 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002198 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002199 if (next_match_m_endpos.lnum == current_lnum
2200 && next_match_m_endpos.col == current_col
2201 && lspp->sp_next_list != NULL)
2202 {
2203 current_next_list = lspp->sp_next_list;
2204 current_next_flags = lspp->sp_flags;
2205 keep_next_list = TRUE;
2206 zero_width_next_list = TRUE;
2207
2208 /* Add the index to a list, so that we can check
2209 * later that we don't match it again (and cause an
2210 * endless loop). */
2211 if (ga_grow(&zero_width_next_ga, 1) == OK)
2212 {
2213 ((int *)(zero_width_next_ga.ga_data))
2214 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002215 }
2216 next_match_idx = -1;
2217 }
2218 else
2219 cur_si = push_next_match(cur_si);
2220 found_match = TRUE;
2221 }
2222 }
2223 }
2224
2225 /*
2226 * Handle searching for nextgroup match.
2227 */
2228 if (current_next_list != NULL && !keep_next_list)
2229 {
2230 /*
2231 * If a nextgroup was not found, continue looking for one if:
2232 * - this is an empty line and the "skipempty" option was given
2233 * - we are on white space and the "skipwhite" option was given
2234 */
2235 if (!found_match)
2236 {
2237 line = syn_getcurline();
2238 if (((current_next_flags & HL_SKIPWHITE)
2239 && vim_iswhite(line[current_col]))
2240 || ((current_next_flags & HL_SKIPEMPTY)
2241 && *line == NUL))
2242 break;
2243 }
2244
2245 /*
2246 * If a nextgroup was found: Use it, and continue looking for
2247 * contained matches.
2248 * If a nextgroup was not found: Continue looking for a normal
2249 * match.
2250 * When did set current_next_list for a zero-width item and no
2251 * match was found don't loop (would get stuck).
2252 */
2253 current_next_list = NULL;
2254 next_match_idx = -1;
2255 if (!zero_width_next_list)
2256 found_match = TRUE;
2257 }
2258
2259 } while (found_match);
2260
2261 /*
2262 * Use attributes from the current state, if within its highlighting.
2263 * If not, use attributes from the current-but-one state, etc.
2264 */
2265 current_attr = 0;
2266#ifdef FEAT_EVAL
2267 current_id = 0;
2268 current_trans_id = 0;
2269#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002270#ifdef FEAT_CONCEAL
2271 current_flags = 0;
2272#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002273 if (cur_si != NULL)
2274 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002275#ifndef FEAT_EVAL
2276 int current_trans_id = 0;
2277#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002278 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2279 {
2280 sip = &CUR_STATE(idx);
2281 if ((current_lnum > sip->si_h_startpos.lnum
2282 || (current_lnum == sip->si_h_startpos.lnum
2283 && current_col >= sip->si_h_startpos.col))
2284 && (sip->si_h_endpos.lnum == 0
2285 || current_lnum < sip->si_h_endpos.lnum
2286 || (current_lnum == sip->si_h_endpos.lnum
2287 && current_col < sip->si_h_endpos.col)))
2288 {
2289 current_attr = sip->si_attr;
2290#ifdef FEAT_EVAL
2291 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002292#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002293 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002294#ifdef FEAT_CONCEAL
2295 current_flags = sip->si_flags;
2296 current_sub_char = sip->si_char;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002297 current_seqnr = sip->si_seqnr;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002298#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002299 break;
2300 }
2301 }
2302
Bram Moolenaar217ad922005-03-20 22:37:15 +00002303 if (can_spell != NULL)
2304 {
2305 struct sp_syn sps;
2306
2307 /*
2308 * set "can_spell" to TRUE if spell checking is supposed to be
2309 * done in the current item.
2310 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002311 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002312 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002313 /* There is no @Spell cluster: Do spelling for items without
2314 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002315 if (syn_block->b_nospell_cluster_id == 0
2316 || current_trans_id == 0)
2317 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002318 else
2319 {
2320 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002321 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002322 sps.cont_in_list = NULL;
2323 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2324 }
2325 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002326 else
2327 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002328 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002329 * the @Spell cluster. But not when @NoSpell is also there.
2330 * At the toplevel only spell check when ":syn spell toplevel"
2331 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002332 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002333 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002334 else
2335 {
2336 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002337 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002338 sps.cont_in_list = NULL;
2339 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2340
Bram Moolenaar860cae12010-06-05 23:22:07 +02002341 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002342 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002343 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002344 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2345 *can_spell = FALSE;
2346 }
2347 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002348 }
2349 }
2350
2351
Bram Moolenaar071d4272004-06-13 20:20:40 +00002352 /*
2353 * Check for end of current state (and the states before it) at the
2354 * next column. Don't do this for syncing, because we would miss a
2355 * single character match.
2356 * First check if the current state ends at the current column. It
2357 * may be for an empty match and a containing item might end in the
2358 * current column.
2359 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002360 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002361 {
2362 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002363 if (current_state.ga_len > 0
2364 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002365 {
2366 ++current_col;
2367 check_state_ends();
2368 --current_col;
2369 }
2370 }
2371 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002372 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002373 /* Default: Only do spelling when there is no @Spell cluster or when
2374 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002375 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2376 ? (syn_block->b_spell_cluster_id == 0)
2377 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002378
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002379 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002380 if (current_next_list != NULL
2381 && syn_getcurline()[current_col + 1] == NUL
2382 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2383 current_next_list = NULL;
2384
2385 if (zero_width_next_ga.ga_len > 0)
2386 ga_clear(&zero_width_next_ga);
2387
2388 /* No longer need external matches. But keep next_match_extmatch. */
2389 unref_extmatch(re_extmatch_out);
2390 re_extmatch_out = NULL;
2391 unref_extmatch(cur_extmatch);
2392
2393 return current_attr;
2394}
2395
2396
2397/*
2398 * Check if we already matched pattern "idx" at the current column.
2399 */
2400 static int
2401did_match_already(idx, gap)
2402 int idx;
2403 garray_T *gap;
2404{
2405 int i;
2406
2407 for (i = current_state.ga_len; --i >= 0; )
2408 if (CUR_STATE(i).si_m_startcol == (int)current_col
2409 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2410 && CUR_STATE(i).si_idx == idx)
2411 return TRUE;
2412
2413 /* Zero-width matches with a nextgroup argument are not put on the syntax
2414 * stack, and can only be matched once anyway. */
2415 for (i = gap->ga_len; --i >= 0; )
2416 if (((int *)(gap->ga_data))[i] == idx)
2417 return TRUE;
2418
2419 return FALSE;
2420}
2421
2422/*
2423 * Push the next match onto the stack.
2424 */
2425 static stateitem_T *
2426push_next_match(cur_si)
2427 stateitem_T *cur_si;
2428{
2429 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002430#ifdef FEAT_CONCEAL
2431 int save_flags;
2432#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002433
Bram Moolenaar860cae12010-06-05 23:22:07 +02002434 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002435
2436 /*
2437 * Push the item in current_state stack;
2438 */
2439 if (push_current_state(next_match_idx) == OK)
2440 {
2441 /*
2442 * If it's a start-skip-end type that crosses lines, figure out how
2443 * much it continues in this line. Otherwise just fill in the length.
2444 */
2445 cur_si = &CUR_STATE(current_state.ga_len - 1);
2446 cur_si->si_h_startpos = next_match_h_startpos;
2447 cur_si->si_m_startcol = current_col;
2448 cur_si->si_m_lnum = current_lnum;
2449 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002450#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002451 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002452 cur_si->si_char = spp->sp_char;
2453 if (current_state.ga_len > 1)
2454 cur_si->si_flags |=
2455 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2456#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002457 cur_si->si_next_list = spp->sp_next_list;
2458 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2459 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2460 {
2461 /* Try to find the end pattern in the current line */
2462 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2463 check_keepend();
2464 }
2465 else
2466 {
2467 cur_si->si_m_endpos = next_match_m_endpos;
2468 cur_si->si_h_endpos = next_match_h_endpos;
2469 cur_si->si_ends = TRUE;
2470 cur_si->si_flags |= next_match_flags;
2471 cur_si->si_eoe_pos = next_match_eoe_pos;
2472 cur_si->si_end_idx = next_match_end_idx;
2473 }
2474 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2475 keepend_level = current_state.ga_len - 1;
2476 check_keepend();
2477 update_si_attr(current_state.ga_len - 1);
2478
Bram Moolenaar860cae12010-06-05 23:22:07 +02002479#ifdef FEAT_CONCEAL
2480 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2481#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002482 /*
2483 * If the start pattern has another highlight group, push another item
2484 * on the stack for the start pattern.
2485 */
2486 if ( spp->sp_type == SPTYPE_START
2487 && spp->sp_syn_match_id != 0
2488 && push_current_state(next_match_idx) == OK)
2489 {
2490 cur_si = &CUR_STATE(current_state.ga_len - 1);
2491 cur_si->si_h_startpos = next_match_h_startpos;
2492 cur_si->si_m_startcol = current_col;
2493 cur_si->si_m_lnum = current_lnum;
2494 cur_si->si_m_endpos = next_match_eos_pos;
2495 cur_si->si_h_endpos = next_match_eos_pos;
2496 cur_si->si_ends = TRUE;
2497 cur_si->si_end_idx = 0;
2498 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002499#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002500 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002501 cur_si->si_flags |= save_flags;
2502 if (cur_si->si_flags & HL_CONCEALENDS)
2503 cur_si->si_flags |= HL_CONCEAL;
2504#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002505 cur_si->si_next_list = NULL;
2506 check_keepend();
2507 update_si_attr(current_state.ga_len - 1);
2508 }
2509 }
2510
2511 next_match_idx = -1; /* try other match next time */
2512
2513 return cur_si;
2514}
2515
2516/*
2517 * Check for end of current state (and the states before it).
2518 */
2519 static void
2520check_state_ends()
2521{
2522 stateitem_T *cur_si;
2523 int had_extend = FALSE;
2524
2525 cur_si = &CUR_STATE(current_state.ga_len - 1);
2526 for (;;)
2527 {
2528 if (cur_si->si_ends
2529 && (cur_si->si_m_endpos.lnum < current_lnum
2530 || (cur_si->si_m_endpos.lnum == current_lnum
2531 && cur_si->si_m_endpos.col <= current_col)))
2532 {
2533 /*
2534 * If there is an end pattern group ID, highlight the end pattern
2535 * now. No need to pop the current item from the stack.
2536 * Only do this if the end pattern continues beyond the current
2537 * position.
2538 */
2539 if (cur_si->si_end_idx
2540 && (cur_si->si_eoe_pos.lnum > current_lnum
2541 || (cur_si->si_eoe_pos.lnum == current_lnum
2542 && cur_si->si_eoe_pos.col > current_col)))
2543 {
2544 cur_si->si_idx = cur_si->si_end_idx;
2545 cur_si->si_end_idx = 0;
2546 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2547 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2548 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002549#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002550 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002551 if (cur_si->si_flags & HL_CONCEALENDS)
2552 cur_si->si_flags |= HL_CONCEAL;
2553#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002554 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002555
2556 /* what matches next may be different now, clear it */
2557 next_match_idx = 0;
2558 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002559 break;
2560 }
2561 else
2562 {
2563 /* handle next_list, unless at end of line and no "skipnl" or
2564 * "skipempty" */
2565 current_next_list = cur_si->si_next_list;
2566 current_next_flags = cur_si->si_flags;
2567 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2568 && syn_getcurline()[current_col] == NUL)
2569 current_next_list = NULL;
2570
2571 /* When the ended item has "extend", another item with
2572 * "keepend" now needs to check for its end. */
2573 if (cur_si->si_flags & HL_EXTEND)
2574 had_extend = TRUE;
2575
2576 pop_current_state();
2577
2578 if (current_state.ga_len == 0)
2579 break;
2580
Bram Moolenaar81993f42008-01-11 20:27:45 +00002581 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002582 {
2583 syn_update_ends(FALSE);
2584 if (current_state.ga_len == 0)
2585 break;
2586 }
2587
2588 cur_si = &CUR_STATE(current_state.ga_len - 1);
2589
2590 /*
2591 * Only for a region the search for the end continues after
2592 * the end of the contained item. If the contained match
2593 * included the end-of-line, break here, the region continues.
2594 * Don't do this when:
2595 * - "keepend" is used for the contained item
2596 * - not at the end of the line (could be end="x$"me=e-1).
2597 * - "excludenl" is used (HL_HAS_EOL won't be set)
2598 */
2599 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002600 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002601 == SPTYPE_START
2602 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2603 {
2604 update_si_end(cur_si, (int)current_col, TRUE);
2605 check_keepend();
2606 if ((current_next_flags & HL_HAS_EOL)
2607 && keepend_level < 0
2608 && syn_getcurline()[current_col] == NUL)
2609 break;
2610 }
2611 }
2612 }
2613 else
2614 break;
2615 }
2616}
2617
2618/*
2619 * Update an entry in the current_state stack for a match or region. This
2620 * fills in si_attr, si_next_list and si_cont_list.
2621 */
2622 static void
2623update_si_attr(idx)
2624 int idx;
2625{
2626 stateitem_T *sip = &CUR_STATE(idx);
2627 synpat_T *spp;
2628
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002629 /* This should not happen... */
2630 if (sip->si_idx < 0)
2631 return;
2632
Bram Moolenaar860cae12010-06-05 23:22:07 +02002633 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002634 if (sip->si_flags & HL_MATCH)
2635 sip->si_id = spp->sp_syn_match_id;
2636 else
2637 sip->si_id = spp->sp_syn.id;
2638 sip->si_attr = syn_id2attr(sip->si_id);
2639 sip->si_trans_id = sip->si_id;
2640 if (sip->si_flags & HL_MATCH)
2641 sip->si_cont_list = NULL;
2642 else
2643 sip->si_cont_list = spp->sp_cont_list;
2644
2645 /*
2646 * For transparent items, take attr from outer item.
2647 * Also take cont_list, if there is none.
2648 * Don't do this for the matchgroup of a start or end pattern.
2649 */
2650 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2651 {
2652 if (idx == 0)
2653 {
2654 sip->si_attr = 0;
2655 sip->si_trans_id = 0;
2656 if (sip->si_cont_list == NULL)
2657 sip->si_cont_list = ID_LIST_ALL;
2658 }
2659 else
2660 {
2661 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2662 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002663 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2664 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002665 if (sip->si_cont_list == NULL)
2666 {
2667 sip->si_flags |= HL_TRANS_CONT;
2668 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2669 }
2670 }
2671 }
2672}
2673
2674/*
2675 * Check the current stack for patterns with "keepend" flag.
2676 * Propagate the match-end to contained items, until a "skipend" item is found.
2677 */
2678 static void
2679check_keepend()
2680{
2681 int i;
2682 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002683 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002684 stateitem_T *sip;
2685
2686 /*
2687 * This check can consume a lot of time; only do it from the level where
2688 * there really is a keepend.
2689 */
2690 if (keepend_level < 0)
2691 return;
2692
2693 /*
2694 * Find the last index of an "extend" item. "keepend" items before that
2695 * won't do anything. If there is no "extend" item "i" will be
2696 * "keepend_level" and all "keepend" items will work normally.
2697 */
2698 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2699 if (CUR_STATE(i).si_flags & HL_EXTEND)
2700 break;
2701
2702 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002703 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002704 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002705 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002706 for ( ; i < current_state.ga_len; ++i)
2707 {
2708 sip = &CUR_STATE(i);
2709 if (maxpos.lnum != 0)
2710 {
2711 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002712 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002713 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2714 sip->si_ends = TRUE;
2715 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002716 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2717 {
2718 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002719 || maxpos.lnum > sip->si_m_endpos.lnum
2720 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002721 && maxpos.col > sip->si_m_endpos.col))
2722 maxpos = sip->si_m_endpos;
2723 if (maxpos_h.lnum == 0
2724 || maxpos_h.lnum > sip->si_h_endpos.lnum
2725 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2726 && maxpos_h.col > sip->si_h_endpos.col))
2727 maxpos_h = sip->si_h_endpos;
2728 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002729 }
2730}
2731
2732/*
2733 * Update an entry in the current_state stack for a start-skip-end pattern.
2734 * This finds the end of the current item, if it's in the current line.
2735 *
2736 * Return the flags for the matched END.
2737 */
2738 static void
2739update_si_end(sip, startcol, force)
2740 stateitem_T *sip;
2741 int startcol; /* where to start searching for the end */
2742 int force; /* when TRUE overrule a previous end */
2743{
2744 lpos_T startpos;
2745 lpos_T endpos;
2746 lpos_T hl_endpos;
2747 lpos_T end_endpos;
2748 int end_idx;
2749
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002750 /* return quickly for a keyword */
2751 if (sip->si_idx < 0)
2752 return;
2753
Bram Moolenaar071d4272004-06-13 20:20:40 +00002754 /* Don't update when it's already done. Can be a match of an end pattern
2755 * that started in a previous line. Watch out: can also be a "keepend"
2756 * from a containing item. */
2757 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2758 return;
2759
2760 /*
2761 * We need to find the end of the region. It may continue in the next
2762 * line.
2763 */
2764 end_idx = 0;
2765 startpos.lnum = current_lnum;
2766 startpos.col = startcol;
2767 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2768 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2769
2770 if (endpos.lnum == 0)
2771 {
2772 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002773 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002774 {
2775 /* a "oneline" never continues in the next line */
2776 sip->si_ends = TRUE;
2777 sip->si_m_endpos.lnum = current_lnum;
2778 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2779 }
2780 else
2781 {
2782 /* continues in the next line */
2783 sip->si_ends = FALSE;
2784 sip->si_m_endpos.lnum = 0;
2785 }
2786 sip->si_h_endpos = sip->si_m_endpos;
2787 }
2788 else
2789 {
2790 /* match within this line */
2791 sip->si_m_endpos = endpos;
2792 sip->si_h_endpos = hl_endpos;
2793 sip->si_eoe_pos = end_endpos;
2794 sip->si_ends = TRUE;
2795 sip->si_end_idx = end_idx;
2796 }
2797}
2798
2799/*
2800 * Add a new state to the current state stack.
2801 * It is cleared and the index set to "idx".
2802 * Return FAIL if it's not possible (out of memory).
2803 */
2804 static int
2805push_current_state(idx)
2806 int idx;
2807{
2808 if (ga_grow(&current_state, 1) == FAIL)
2809 return FAIL;
2810 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2811 CUR_STATE(current_state.ga_len).si_idx = idx;
2812 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002813 return OK;
2814}
2815
2816/*
2817 * Remove a state from the current_state stack.
2818 */
2819 static void
2820pop_current_state()
2821{
2822 if (current_state.ga_len)
2823 {
2824 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2825 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002826 }
2827 /* after the end of a pattern, try matching a keyword or pattern */
2828 next_match_idx = -1;
2829
2830 /* if first state with "keepend" is popped, reset keepend_level */
2831 if (keepend_level >= current_state.ga_len)
2832 keepend_level = -1;
2833}
2834
2835/*
2836 * Find the end of a start/skip/end syntax region after "startpos".
2837 * Only checks one line.
2838 * Also handles a match item that continued from a previous line.
2839 * If not found, the syntax item continues in the next line. m_endpos->lnum
2840 * will be 0.
2841 * If found, the end of the region and the end of the highlighting is
2842 * computed.
2843 */
2844 static void
2845find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2846 end_idx, start_ext)
2847 int idx; /* index of the pattern */
2848 lpos_T *startpos; /* where to start looking for an END match */
2849 lpos_T *m_endpos; /* return: end of match */
2850 lpos_T *hl_endpos; /* return: end of highlighting */
2851 long *flagsp; /* return: flags of matching END */
2852 lpos_T *end_endpos; /* return: end of end pattern match */
2853 int *end_idx; /* return: group ID for end pat. match, or 0 */
2854 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2855{
2856 colnr_T matchcol;
2857 synpat_T *spp, *spp_skip;
2858 int start_idx;
2859 int best_idx;
2860 regmmatch_T regmatch;
2861 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2862 lpos_T pos;
2863 char_u *line;
2864 int had_match = FALSE;
2865
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002866 /* just in case we are invoked for a keyword */
2867 if (idx < 0)
2868 return;
2869
Bram Moolenaar071d4272004-06-13 20:20:40 +00002870 /*
2871 * Check for being called with a START pattern.
2872 * Can happen with a match that continues to the next line, because it
2873 * contained a region.
2874 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002875 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002876 if (spp->sp_type != SPTYPE_START)
2877 {
2878 *hl_endpos = *startpos;
2879 return;
2880 }
2881
2882 /*
2883 * Find the SKIP or first END pattern after the last START pattern.
2884 */
2885 for (;;)
2886 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002887 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002888 if (spp->sp_type != SPTYPE_START)
2889 break;
2890 ++idx;
2891 }
2892
2893 /*
2894 * Lookup the SKIP pattern (if present)
2895 */
2896 if (spp->sp_type == SPTYPE_SKIP)
2897 {
2898 spp_skip = spp;
2899 ++idx;
2900 }
2901 else
2902 spp_skip = NULL;
2903
2904 /* Setup external matches for syn_regexec(). */
2905 unref_extmatch(re_extmatch_in);
2906 re_extmatch_in = ref_extmatch(start_ext);
2907
2908 matchcol = startpos->col; /* start looking for a match at sstart */
2909 start_idx = idx; /* remember the first END pattern. */
2910 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2911 for (;;)
2912 {
2913 /*
2914 * Find end pattern that matches first after "matchcol".
2915 */
2916 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002917 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002918 {
2919 int lc_col = matchcol;
2920
Bram Moolenaar860cae12010-06-05 23:22:07 +02002921 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002922 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2923 break;
2924 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2925 if (lc_col < 0)
2926 lc_col = 0;
2927
2928 regmatch.rmm_ic = spp->sp_ic;
2929 regmatch.regprog = spp->sp_prog;
2930 if (syn_regexec(&regmatch, startpos->lnum, lc_col))
2931 {
2932 if (best_idx == -1 || regmatch.startpos[0].col
2933 < best_regmatch.startpos[0].col)
2934 {
2935 best_idx = idx;
2936 best_regmatch.startpos[0] = regmatch.startpos[0];
2937 best_regmatch.endpos[0] = regmatch.endpos[0];
2938 }
2939 }
2940 }
2941
2942 /*
2943 * If all end patterns have been tried, and there is no match, the
2944 * item continues until end-of-line.
2945 */
2946 if (best_idx == -1)
2947 break;
2948
2949 /*
2950 * If the skip pattern matches before the end pattern,
2951 * continue searching after the skip pattern.
2952 */
2953 if (spp_skip != NULL)
2954 {
2955 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2956
2957 if (lc_col < 0)
2958 lc_col = 0;
2959 regmatch.rmm_ic = spp_skip->sp_ic;
2960 regmatch.regprog = spp_skip->sp_prog;
2961 if (syn_regexec(&regmatch, startpos->lnum, lc_col)
2962 && regmatch.startpos[0].col
2963 <= best_regmatch.startpos[0].col)
2964 {
2965 /* Add offset to skip pattern match */
2966 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2967
2968 /* If the skip pattern goes on to the next line, there is no
2969 * match with an end pattern in this line. */
2970 if (pos.lnum > startpos->lnum)
2971 break;
2972
2973 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
2974
2975 /* take care of an empty match or negative offset */
2976 if (pos.col <= matchcol)
2977 ++matchcol;
2978 else if (pos.col <= regmatch.endpos[0].col)
2979 matchcol = pos.col;
2980 else
2981 /* Be careful not to jump over the NUL at the end-of-line */
2982 for (matchcol = regmatch.endpos[0].col;
2983 line[matchcol] != NUL && matchcol < pos.col;
2984 ++matchcol)
2985 ;
2986
2987 /* if the skip pattern includes end-of-line, break here */
2988 if (line[matchcol] == NUL)
2989 break;
2990
2991 continue; /* start with first end pattern again */
2992 }
2993 }
2994
2995 /*
2996 * Match from start pattern to end pattern.
2997 * Correct for match and highlight offset of end pattern.
2998 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002999 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003000 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3001 /* can't end before the start */
3002 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3003 m_endpos->col = startpos->col;
3004
3005 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3006 /* can't end before the start */
3007 if (end_endpos->lnum == startpos->lnum
3008 && end_endpos->col < startpos->col)
3009 end_endpos->col = startpos->col;
3010 /* can't end after the match */
3011 limit_pos(end_endpos, m_endpos);
3012
3013 /*
3014 * If the end group is highlighted differently, adjust the pointers.
3015 */
3016 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3017 {
3018 *end_idx = best_idx;
3019 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3020 {
3021 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3022 hl_endpos->col = best_regmatch.endpos[0].col;
3023 }
3024 else
3025 {
3026 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3027 hl_endpos->col = best_regmatch.startpos[0].col;
3028 }
3029 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3030
3031 /* can't end before the start */
3032 if (hl_endpos->lnum == startpos->lnum
3033 && hl_endpos->col < startpos->col)
3034 hl_endpos->col = startpos->col;
3035 limit_pos(hl_endpos, m_endpos);
3036
3037 /* now the match ends where the highlighting ends, it is turned
3038 * into the matchgroup for the end */
3039 *m_endpos = *hl_endpos;
3040 }
3041 else
3042 {
3043 *end_idx = 0;
3044 *hl_endpos = *end_endpos;
3045 }
3046
3047 *flagsp = spp->sp_flags;
3048
3049 had_match = TRUE;
3050 break;
3051 }
3052
3053 /* no match for an END pattern in this line */
3054 if (!had_match)
3055 m_endpos->lnum = 0;
3056
3057 /* Remove external matches. */
3058 unref_extmatch(re_extmatch_in);
3059 re_extmatch_in = NULL;
3060}
3061
3062/*
3063 * Limit "pos" not to be after "limit".
3064 */
3065 static void
3066limit_pos(pos, limit)
3067 lpos_T *pos;
3068 lpos_T *limit;
3069{
3070 if (pos->lnum > limit->lnum)
3071 *pos = *limit;
3072 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3073 pos->col = limit->col;
3074}
3075
3076/*
3077 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3078 */
3079 static void
3080limit_pos_zero(pos, limit)
3081 lpos_T *pos;
3082 lpos_T *limit;
3083{
3084 if (pos->lnum == 0)
3085 *pos = *limit;
3086 else
3087 limit_pos(pos, limit);
3088}
3089
3090/*
3091 * Add offset to matched text for end of match or highlight.
3092 */
3093 static void
3094syn_add_end_off(result, regmatch, spp, idx, extra)
3095 lpos_T *result; /* returned position */
3096 regmmatch_T *regmatch; /* start/end of match */
3097 synpat_T *spp; /* matched pattern */
3098 int idx; /* index of offset */
3099 int extra; /* extra chars for offset to start */
3100{
3101 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003102 int off;
3103 char_u *base;
3104 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003105
3106 if (spp->sp_off_flags & (1 << idx))
3107 {
3108 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003109 col = regmatch->startpos[0].col;
3110 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003111 }
3112 else
3113 {
3114 result->lnum = regmatch->endpos[0].lnum;
3115 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003116 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003117 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003118 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3119 * is a matchgroup. Watch out for match with last NL in the buffer. */
3120 if (result->lnum > syn_buf->b_ml.ml_line_count)
3121 col = 0;
3122 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003123 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003124 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3125 p = base + col;
3126 if (off > 0)
3127 {
3128 while (off-- > 0 && *p != NUL)
3129 mb_ptr_adv(p);
3130 }
3131 else if (off < 0)
3132 {
3133 while (off++ < 0 && base < p)
3134 mb_ptr_back(base, p);
3135 }
3136 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003137 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003138 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003139}
3140
3141/*
3142 * Add offset to matched text for start of match or highlight.
3143 * Avoid resulting column to become negative.
3144 */
3145 static void
3146syn_add_start_off(result, regmatch, spp, idx, extra)
3147 lpos_T *result; /* returned position */
3148 regmmatch_T *regmatch; /* start/end of match */
3149 synpat_T *spp;
3150 int idx;
3151 int extra; /* extra chars for offset to end */
3152{
3153 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003154 int off;
3155 char_u *base;
3156 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003157
3158 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3159 {
3160 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003161 col = regmatch->endpos[0].col;
3162 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003163 }
3164 else
3165 {
3166 result->lnum = regmatch->startpos[0].lnum;
3167 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003168 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003169 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003170 if (result->lnum > syn_buf->b_ml.ml_line_count)
3171 {
3172 /* a "\n" at the end of the pattern may take us below the last line */
3173 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003174 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003175 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003176 if (off != 0)
3177 {
3178 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3179 p = base + col;
3180 if (off > 0)
3181 {
3182 while (off-- && *p != NUL)
3183 mb_ptr_adv(p);
3184 }
3185 else if (off < 0)
3186 {
3187 while (off++ && base < p)
3188 mb_ptr_back(base, p);
3189 }
3190 col = (int)(p - base);
3191 }
3192 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003193}
3194
3195/*
3196 * Get current line in syntax buffer.
3197 */
3198 static char_u *
3199syn_getcurline()
3200{
3201 return ml_get_buf(syn_buf, current_lnum, FALSE);
3202}
3203
3204/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003205 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003206 * Returns TRUE when there is a match.
3207 */
3208 static int
3209syn_regexec(rmp, lnum, col)
3210 regmmatch_T *rmp;
3211 linenr_T lnum;
3212 colnr_T col;
3213{
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003214 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar91a4e822008-01-19 14:59:58 +00003215 if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003216 {
3217 rmp->startpos[0].lnum += lnum;
3218 rmp->endpos[0].lnum += lnum;
3219 return TRUE;
3220 }
3221 return FALSE;
3222}
3223
3224/*
3225 * Check one position in a line for a matching keyword.
3226 * The caller must check if a keyword can start at startcol.
3227 * Return it's ID if found, 0 otherwise.
3228 */
3229 static int
Bram Moolenaar860cae12010-06-05 23:22:07 +02003230check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si, ccharp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003231 char_u *line;
3232 int startcol; /* position in line to check for keyword */
3233 int *endcolp; /* return: character after found keyword */
3234 long *flagsp; /* return: flags of matching keyword */
3235 short **next_listp; /* return: next_list of matching keyword */
3236 stateitem_T *cur_si; /* item at the top of the stack */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003237 int *ccharp UNUSED; /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003238{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003239 keyentry_T *kp;
3240 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003241 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003242 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003243 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003244 hashtab_T *ht;
3245 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003246
3247 /* Find first character after the keyword. First character was already
3248 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003249 kwp = line + startcol;
3250 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003251 do
3252 {
3253#ifdef FEAT_MBYTE
3254 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003255 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003256 else
3257#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003258 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003259 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003260 while (vim_iswordc_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003261
Bram Moolenaardad6b692005-01-25 22:14:34 +00003262 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003263 return 0;
3264
3265 /*
3266 * Must make a copy of the keyword, so we can add a NUL and make it
3267 * lowercase.
3268 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003269 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003270
3271 /*
3272 * Try twice:
3273 * 1. matching case
3274 * 2. ignoring case
3275 */
3276 for (round = 1; round <= 2; ++round)
3277 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003278 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003279 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003280 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003281 if (round == 2) /* ignore case */
3282 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003283
3284 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003285 * Find keywords that match. There can be several with different
3286 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003287 * When current_next_list is non-zero accept only that group, otherwise:
3288 * Accept a not-contained keyword at toplevel.
3289 * Accept a keyword at other levels only if it is in the contains list.
3290 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003291 hi = hash_find(ht, keyword);
3292 if (!HASHITEM_EMPTY(hi))
3293 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003294 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003295 if (current_next_list != 0
3296 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3297 : (cur_si == NULL
3298 ? !(kp->flags & HL_CONTAINED)
3299 : in_id_list(cur_si, cur_si->si_cont_list,
3300 &kp->k_syn, kp->flags & HL_CONTAINED)))
3301 {
3302 *endcolp = startcol + kwlen;
3303 *flagsp = kp->flags;
3304 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003305#ifdef FEAT_CONCEAL
3306 *ccharp = kp->k_char;
3307#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003308 return kp->k_syn.id;
3309 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003310 }
3311 }
3312 return 0;
3313}
3314
3315/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003316 * Handle ":syntax conceal" command.
3317 */
3318 static void
3319syn_cmd_conceal(eap, syncing)
3320 exarg_T *eap UNUSED;
3321 int syncing UNUSED;
3322{
3323#ifdef FEAT_CONCEAL
3324 char_u *arg = eap->arg;
3325 char_u *next;
3326
3327 eap->nextcmd = find_nextcmd(arg);
3328 if (eap->skip)
3329 return;
3330
3331 next = skiptowhite(arg);
3332 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3333 curwin->w_s->b_syn_conceal = TRUE;
3334 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3335 curwin->w_s->b_syn_conceal = FALSE;
3336 else
3337 EMSG2(_("E390: Illegal argument: %s"), arg);
3338#endif
3339}
3340
3341/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003342 * Handle ":syntax case" command.
3343 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003344 static void
3345syn_cmd_case(eap, syncing)
3346 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003347 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003348{
3349 char_u *arg = eap->arg;
3350 char_u *next;
3351
3352 eap->nextcmd = find_nextcmd(arg);
3353 if (eap->skip)
3354 return;
3355
3356 next = skiptowhite(arg);
3357 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003358 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003359 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003360 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003361 else
3362 EMSG2(_("E390: Illegal argument: %s"), arg);
3363}
3364
3365/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003366 * Handle ":syntax spell" command.
3367 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003368 static void
3369syn_cmd_spell(eap, syncing)
3370 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003371 int syncing UNUSED;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003372{
3373 char_u *arg = eap->arg;
3374 char_u *next;
3375
3376 eap->nextcmd = find_nextcmd(arg);
3377 if (eap->skip)
3378 return;
3379
3380 next = skiptowhite(arg);
3381 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003382 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003383 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003384 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003385 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003386 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003387 else
3388 EMSG2(_("E390: Illegal argument: %s"), arg);
3389}
3390
3391/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003392 * Clear all syntax info for one buffer.
3393 */
3394 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003395syntax_clear(block)
3396 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003397{
3398 int i;
3399
Bram Moolenaar860cae12010-06-05 23:22:07 +02003400 block->b_syn_error = FALSE; /* clear previous error */
3401 block->b_syn_ic = FALSE; /* Use case, by default */
3402 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3403 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003404
3405 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003406 clear_keywtab(&block->b_keywtab);
3407 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003408
3409 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003410 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3411 syn_clear_pattern(block, i);
3412 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003413
3414 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003415 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3416 syn_clear_cluster(block, i);
3417 ga_clear(&block->b_syn_clusters);
3418 block->b_spell_cluster_id = 0;
3419 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003420
Bram Moolenaar860cae12010-06-05 23:22:07 +02003421 block->b_syn_sync_flags = 0;
3422 block->b_syn_sync_minlines = 0;
3423 block->b_syn_sync_maxlines = 0;
3424 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003425
Bram Moolenaar860cae12010-06-05 23:22:07 +02003426 vim_free(block->b_syn_linecont_prog);
3427 block->b_syn_linecont_prog = NULL;
3428 vim_free(block->b_syn_linecont_pat);
3429 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003430#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003431 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003432#endif
3433
3434 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003435 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003436 invalidate_current_state();
3437}
3438
3439/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003440 * Get rid of ownsyntax for window "wp".
3441 */
3442 void
3443reset_synblock(wp)
3444 win_T *wp;
3445{
3446 if (wp->w_s != &wp->w_buffer->b_s)
3447 {
3448 syntax_clear(wp->w_s);
3449 vim_free(wp->w_s);
3450 wp->w_s = &wp->w_buffer->b_s;
3451 }
3452}
3453
3454/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003455 * Clear syncing info for one buffer.
3456 */
3457 static void
3458syntax_sync_clear()
3459{
3460 int i;
3461
3462 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003463 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3464 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3465 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003466
Bram Moolenaar860cae12010-06-05 23:22:07 +02003467 curwin->w_s->b_syn_sync_flags = 0;
3468 curwin->w_s->b_syn_sync_minlines = 0;
3469 curwin->w_s->b_syn_sync_maxlines = 0;
3470 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003471
Bram Moolenaar860cae12010-06-05 23:22:07 +02003472 vim_free(curwin->w_s->b_syn_linecont_prog);
3473 curwin->w_s->b_syn_linecont_prog = NULL;
3474 vim_free(curwin->w_s->b_syn_linecont_pat);
3475 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003476
Bram Moolenaar860cae12010-06-05 23:22:07 +02003477 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003478}
3479
3480/*
3481 * Remove one pattern from the buffer's pattern list.
3482 */
3483 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003484syn_remove_pattern(block, idx)
3485 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003486 int idx;
3487{
3488 synpat_T *spp;
3489
Bram Moolenaar860cae12010-06-05 23:22:07 +02003490 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003491#ifdef FEAT_FOLDING
3492 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003493 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003494#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003495 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003496 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003497 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3498 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003499}
3500
3501/*
3502 * Clear and free one syntax pattern. When clearing all, must be called from
3503 * last to first!
3504 */
3505 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003506syn_clear_pattern(block, i)
3507 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003508 int i;
3509{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003510 vim_free(SYN_ITEMS(block)[i].sp_pattern);
3511 vim_free(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003512 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003513 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003514 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003515 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3516 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3517 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003518 }
3519}
3520
3521/*
3522 * Clear and free one syntax cluster.
3523 */
3524 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003525syn_clear_cluster(block, i)
3526 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003527 int i;
3528{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003529 vim_free(SYN_CLSTR(block)[i].scl_name);
3530 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3531 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003532}
3533
3534/*
3535 * Handle ":syntax clear" command.
3536 */
3537 static void
3538syn_cmd_clear(eap, syncing)
3539 exarg_T *eap;
3540 int syncing;
3541{
3542 char_u *arg = eap->arg;
3543 char_u *arg_end;
3544 int id;
3545
3546 eap->nextcmd = find_nextcmd(arg);
3547 if (eap->skip)
3548 return;
3549
3550 /*
3551 * We have to disable this within ":syn include @group filename",
3552 * because otherwise @group would get deleted.
3553 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3554 * clear".
3555 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003556 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003557 return;
3558
3559 if (ends_excmd(*arg))
3560 {
3561 /*
3562 * No argument: Clear all syntax items.
3563 */
3564 if (syncing)
3565 syntax_sync_clear();
3566 else
3567 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003568 syntax_clear(curwin->w_s);
3569 if (curwin->w_s == &curwin->w_buffer->b_s)
3570 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003571 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003572 }
3573 }
3574 else
3575 {
3576 /*
3577 * Clear the group IDs that are in the argument.
3578 */
3579 while (!ends_excmd(*arg))
3580 {
3581 arg_end = skiptowhite(arg);
3582 if (*arg == '@')
3583 {
3584 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3585 if (id == 0)
3586 {
3587 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3588 break;
3589 }
3590 else
3591 {
3592 /*
3593 * We can't physically delete a cluster without changing
3594 * the IDs of other clusters, so we do the next best thing
3595 * and make it empty.
3596 */
3597 short scl_id = id - SYNID_CLUSTER;
3598
Bram Moolenaar860cae12010-06-05 23:22:07 +02003599 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3600 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003601 }
3602 }
3603 else
3604 {
3605 id = syn_namen2id(arg, (int)(arg_end - arg));
3606 if (id == 0)
3607 {
3608 EMSG2(_(e_nogroup), arg);
3609 break;
3610 }
3611 else
3612 syn_clear_one(id, syncing);
3613 }
3614 arg = skipwhite(arg_end);
3615 }
3616 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003617 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003618 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003619}
3620
3621/*
3622 * Clear one syntax group for the current buffer.
3623 */
3624 static void
3625syn_clear_one(id, syncing)
3626 int id;
3627 int syncing;
3628{
3629 synpat_T *spp;
3630 int idx;
3631
3632 /* Clear keywords only when not ":syn sync clear group-name" */
3633 if (!syncing)
3634 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003635 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3636 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003637 }
3638
3639 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003640 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003641 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003642 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003643 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3644 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003645 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003646 }
3647}
3648
3649/*
3650 * Handle ":syntax on" command.
3651 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003652 static void
3653syn_cmd_on(eap, syncing)
3654 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003655 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003656{
3657 syn_cmd_onoff(eap, "syntax");
3658}
3659
3660/*
3661 * Handle ":syntax enable" command.
3662 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003663 static void
3664syn_cmd_enable(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 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3669 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003670 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003671}
3672
3673/*
3674 * Handle ":syntax reset" command.
3675 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003676 static void
3677syn_cmd_reset(eap, syncing)
3678 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003679 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003680{
3681 eap->nextcmd = check_nextcmd(eap->arg);
3682 if (!eap->skip)
3683 {
3684 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3685 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003686 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003687 }
3688}
3689
3690/*
3691 * Handle ":syntax manual" command.
3692 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003693 static void
3694syn_cmd_manual(eap, syncing)
3695 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003696 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003697{
3698 syn_cmd_onoff(eap, "manual");
3699}
3700
3701/*
3702 * Handle ":syntax off" command.
3703 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003704 static void
3705syn_cmd_off(eap, syncing)
3706 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003707 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003708{
3709 syn_cmd_onoff(eap, "nosyntax");
3710}
3711
3712 static void
3713syn_cmd_onoff(eap, name)
3714 exarg_T *eap;
3715 char *name;
3716{
3717 char_u buf[100];
3718
3719 eap->nextcmd = check_nextcmd(eap->arg);
3720 if (!eap->skip)
3721 {
3722 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003723 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003724 do_cmdline_cmd(buf);
3725 }
3726}
3727
3728/*
3729 * Handle ":syntax [list]" command: list current syntax words.
3730 */
3731 static void
3732syn_cmd_list(eap, syncing)
3733 exarg_T *eap;
3734 int syncing; /* when TRUE: list syncing items */
3735{
3736 char_u *arg = eap->arg;
3737 int id;
3738 char_u *arg_end;
3739
3740 eap->nextcmd = find_nextcmd(arg);
3741 if (eap->skip)
3742 return;
3743
Bram Moolenaar860cae12010-06-05 23:22:07 +02003744 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003745 {
3746 MSG(_("No Syntax items defined for this buffer"));
3747 return;
3748 }
3749
3750 if (syncing)
3751 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003752 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003753 {
3754 MSG_PUTS(_("syncing on C-style comments"));
3755 syn_lines_msg();
3756 syn_match_msg();
3757 return;
3758 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003759 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003760 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003761 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003762 MSG_PUTS(_("no syncing"));
3763 else
3764 {
3765 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003766 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003767 MSG_PUTS(_(" lines before top line"));
3768 syn_match_msg();
3769 }
3770 return;
3771 }
3772 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003773 if (curwin->w_s->b_syn_sync_minlines > 0
3774 || curwin->w_s->b_syn_sync_maxlines > 0
3775 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003776 {
3777 MSG_PUTS(_("\nsyncing on items"));
3778 syn_lines_msg();
3779 syn_match_msg();
3780 }
3781 }
3782 else
3783 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3784 if (ends_excmd(*arg))
3785 {
3786 /*
3787 * No argument: List all group IDs and all syntax clusters.
3788 */
3789 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3790 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003791 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003792 syn_list_cluster(id);
3793 }
3794 else
3795 {
3796 /*
3797 * List the group IDs and syntax clusters that are in the argument.
3798 */
3799 while (!ends_excmd(*arg) && !got_int)
3800 {
3801 arg_end = skiptowhite(arg);
3802 if (*arg == '@')
3803 {
3804 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3805 if (id == 0)
3806 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3807 else
3808 syn_list_cluster(id - SYNID_CLUSTER);
3809 }
3810 else
3811 {
3812 id = syn_namen2id(arg, (int)(arg_end - arg));
3813 if (id == 0)
3814 EMSG2(_(e_nogroup), arg);
3815 else
3816 syn_list_one(id, syncing, TRUE);
3817 }
3818 arg = skipwhite(arg_end);
3819 }
3820 }
3821 eap->nextcmd = check_nextcmd(arg);
3822}
3823
3824 static void
3825syn_lines_msg()
3826{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003827 if (curwin->w_s->b_syn_sync_maxlines > 0
3828 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003829 {
3830 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003831 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003832 {
3833 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003834 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3835 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003836 MSG_PUTS(", ");
3837 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003838 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003839 {
3840 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003841 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003842 }
3843 MSG_PUTS(_(" lines before top line"));
3844 }
3845}
3846
3847 static void
3848syn_match_msg()
3849{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003850 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003851 {
3852 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003853 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003854 MSG_PUTS(_(" line breaks"));
3855 }
3856}
3857
3858static int last_matchgroup;
3859
3860struct name_list
3861{
3862 int flag;
3863 char *name;
3864};
3865
3866static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3867
3868/*
3869 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3870 */
3871 static void
3872syn_list_one(id, syncing, link_only)
3873 int id;
3874 int syncing; /* when TRUE: list syncing items */
3875 int link_only; /* when TRUE; list link-only too */
3876{
3877 int attr;
3878 int idx;
3879 int did_header = FALSE;
3880 synpat_T *spp;
3881 static struct name_list namelist1[] =
3882 {
3883 {HL_DISPLAY, "display"},
3884 {HL_CONTAINED, "contained"},
3885 {HL_ONELINE, "oneline"},
3886 {HL_KEEPEND, "keepend"},
3887 {HL_EXTEND, "extend"},
3888 {HL_EXCLUDENL, "excludenl"},
3889 {HL_TRANSP, "transparent"},
3890 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003891#ifdef FEAT_CONCEAL
3892 {HL_CONCEAL, "conceal"},
3893 {HL_CONCEALENDS, "concealends"},
3894#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003895 {0, NULL}
3896 };
3897 static struct name_list namelist2[] =
3898 {
3899 {HL_SKIPWHITE, "skipwhite"},
3900 {HL_SKIPNL, "skipnl"},
3901 {HL_SKIPEMPTY, "skipempty"},
3902 {0, NULL}
3903 };
3904
3905 attr = hl_attr(HLF_D); /* highlight like directories */
3906
3907 /* list the keywords for "id" */
3908 if (!syncing)
3909 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003910 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3911 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003912 did_header, attr);
3913 }
3914
3915 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003916 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003917 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003918 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003919 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3920 continue;
3921
3922 (void)syn_list_header(did_header, 999, id);
3923 did_header = TRUE;
3924 last_matchgroup = 0;
3925 if (spp->sp_type == SPTYPE_MATCH)
3926 {
3927 put_pattern("match", ' ', spp, attr);
3928 msg_putchar(' ');
3929 }
3930 else if (spp->sp_type == SPTYPE_START)
3931 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003932 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
3933 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3934 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
3935 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3936 while (idx < curwin->w_s->b_syn_patterns.ga_len
3937 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
3938 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003939 --idx;
3940 msg_putchar(' ');
3941 }
3942 syn_list_flags(namelist1, spp->sp_flags, attr);
3943
3944 if (spp->sp_cont_list != NULL)
3945 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3946
3947 if (spp->sp_syn.cont_in_list != NULL)
3948 put_id_list((char_u *)"containedin",
3949 spp->sp_syn.cont_in_list, attr);
3950
3951 if (spp->sp_next_list != NULL)
3952 {
3953 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3954 syn_list_flags(namelist2, spp->sp_flags, attr);
3955 }
3956 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3957 {
3958 if (spp->sp_flags & HL_SYNC_HERE)
3959 msg_puts_attr((char_u *)"grouphere", attr);
3960 else
3961 msg_puts_attr((char_u *)"groupthere", attr);
3962 msg_putchar(' ');
3963 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003964 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003965 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3966 else
3967 MSG_PUTS("NONE");
3968 msg_putchar(' ');
3969 }
3970 }
3971
3972 /* list the link, if there is one */
3973 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3974 {
3975 (void)syn_list_header(did_header, 999, id);
3976 msg_puts_attr((char_u *)"links to", attr);
3977 msg_putchar(' ');
3978 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3979 }
3980}
3981
3982 static void
3983syn_list_flags(nl, flags, attr)
3984 struct name_list *nl;
3985 int flags;
3986 int attr;
3987{
3988 int i;
3989
3990 for (i = 0; nl[i].flag != 0; ++i)
3991 if (flags & nl[i].flag)
3992 {
3993 msg_puts_attr((char_u *)nl[i].name, attr);
3994 msg_putchar(' ');
3995 }
3996}
3997
3998/*
3999 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4000 */
4001 static void
4002syn_list_cluster(id)
4003 int id;
4004{
4005 int endcol = 15;
4006
4007 /* slight hack: roughly duplicate the guts of syn_list_header() */
4008 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004009 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004010
4011 if (msg_col >= endcol) /* output at least one space */
4012 endcol = msg_col + 1;
4013 if (Columns <= endcol) /* avoid hang for tiny window */
4014 endcol = Columns - 1;
4015
4016 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004017 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004018 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004019 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004020 hl_attr(HLF_D));
4021 }
4022 else
4023 {
4024 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4025 msg_puts((char_u *)"=NONE");
4026 }
4027}
4028
4029 static void
4030put_id_list(name, list, attr)
4031 char_u *name;
4032 short *list;
4033 int attr;
4034{
4035 short *p;
4036
4037 msg_puts_attr(name, attr);
4038 msg_putchar('=');
4039 for (p = list; *p; ++p)
4040 {
4041 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4042 {
4043 if (p[1])
4044 MSG_PUTS("ALLBUT");
4045 else
4046 MSG_PUTS("ALL");
4047 }
4048 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4049 {
4050 MSG_PUTS("TOP");
4051 }
4052 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4053 {
4054 MSG_PUTS("CONTAINED");
4055 }
4056 else if (*p >= SYNID_CLUSTER)
4057 {
4058 short scl_id = *p - SYNID_CLUSTER;
4059
4060 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004061 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004062 }
4063 else
4064 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4065 if (p[1])
4066 msg_putchar(',');
4067 }
4068 msg_putchar(' ');
4069}
4070
4071 static void
4072put_pattern(s, c, spp, attr)
4073 char *s;
4074 int c;
4075 synpat_T *spp;
4076 int attr;
4077{
4078 long n;
4079 int mask;
4080 int first;
4081 static char *sepchars = "/+=-#@\"|'^&";
4082 int i;
4083
4084 /* May have to write "matchgroup=group" */
4085 if (last_matchgroup != spp->sp_syn_match_id)
4086 {
4087 last_matchgroup = spp->sp_syn_match_id;
4088 msg_puts_attr((char_u *)"matchgroup", attr);
4089 msg_putchar('=');
4090 if (last_matchgroup == 0)
4091 msg_outtrans((char_u *)"NONE");
4092 else
4093 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4094 msg_putchar(' ');
4095 }
4096
4097 /* output the name of the pattern and an '=' or ' ' */
4098 msg_puts_attr((char_u *)s, attr);
4099 msg_putchar(c);
4100
4101 /* output the pattern, in between a char that is not in the pattern */
4102 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4103 if (sepchars[++i] == NUL)
4104 {
4105 i = 0; /* no good char found, just use the first one */
4106 break;
4107 }
4108 msg_putchar(sepchars[i]);
4109 msg_outtrans(spp->sp_pattern);
4110 msg_putchar(sepchars[i]);
4111
4112 /* output any pattern options */
4113 first = TRUE;
4114 for (i = 0; i < SPO_COUNT; ++i)
4115 {
4116 mask = (1 << i);
4117 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4118 {
4119 if (!first)
4120 msg_putchar(','); /* separate with commas */
4121 msg_puts((char_u *)spo_name_tab[i]);
4122 n = spp->sp_offsets[i];
4123 if (i != SPO_LC_OFF)
4124 {
4125 if (spp->sp_off_flags & mask)
4126 msg_putchar('s');
4127 else
4128 msg_putchar('e');
4129 if (n > 0)
4130 msg_putchar('+');
4131 }
4132 if (n || i == SPO_LC_OFF)
4133 msg_outnum(n);
4134 first = FALSE;
4135 }
4136 }
4137 msg_putchar(' ');
4138}
4139
4140/*
4141 * List or clear the keywords for one syntax group.
4142 * Return TRUE if the header has been printed.
4143 */
4144 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00004145syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004146 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004147 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004148 int did_header; /* header has already been printed */
4149 int attr;
4150{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004151 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004152 hashitem_T *hi;
4153 keyentry_T *kp;
4154 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004155 int prev_contained = 0;
4156 short *prev_next_list = NULL;
4157 short *prev_cont_in_list = NULL;
4158 int prev_skipnl = 0;
4159 int prev_skipwhite = 0;
4160 int prev_skipempty = 0;
4161
Bram Moolenaar071d4272004-06-13 20:20:40 +00004162 /*
4163 * Unfortunately, this list of keywords is not sorted on alphabet but on
4164 * hash value...
4165 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004166 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004167 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004168 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004169 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004170 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004171 --todo;
4172 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004173 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004174 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004175 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004176 if (prev_contained != (kp->flags & HL_CONTAINED)
4177 || prev_skipnl != (kp->flags & HL_SKIPNL)
4178 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4179 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4180 || prev_cont_in_list != kp->k_syn.cont_in_list
4181 || prev_next_list != kp->next_list)
4182 outlen = 9999;
4183 else
4184 outlen = (int)STRLEN(kp->keyword);
4185 /* output "contained" and "nextgroup" on each line */
4186 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004187 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004188 prev_contained = 0;
4189 prev_next_list = NULL;
4190 prev_cont_in_list = NULL;
4191 prev_skipnl = 0;
4192 prev_skipwhite = 0;
4193 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004194 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004195 did_header = TRUE;
4196 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004197 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004198 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004199 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004200 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004201 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004202 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004203 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004204 put_id_list((char_u *)"containedin",
4205 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004206 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004207 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004208 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004209 if (kp->next_list != prev_next_list)
4210 {
4211 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4212 msg_putchar(' ');
4213 prev_next_list = kp->next_list;
4214 if (kp->flags & HL_SKIPNL)
4215 {
4216 msg_puts_attr((char_u *)"skipnl", attr);
4217 msg_putchar(' ');
4218 prev_skipnl = (kp->flags & HL_SKIPNL);
4219 }
4220 if (kp->flags & HL_SKIPWHITE)
4221 {
4222 msg_puts_attr((char_u *)"skipwhite", attr);
4223 msg_putchar(' ');
4224 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4225 }
4226 if (kp->flags & HL_SKIPEMPTY)
4227 {
4228 msg_puts_attr((char_u *)"skipempty", attr);
4229 msg_putchar(' ');
4230 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4231 }
4232 }
4233 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004234 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004235 }
4236 }
4237 }
4238
4239 return did_header;
4240}
4241
4242 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004243syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004244 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004245 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004246{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004247 hashitem_T *hi;
4248 keyentry_T *kp;
4249 keyentry_T *kp_prev;
4250 keyentry_T *kp_next;
4251 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004252
Bram Moolenaardad6b692005-01-25 22:14:34 +00004253 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004254 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004255 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004256 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004257 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004258 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004259 --todo;
4260 kp_prev = NULL;
4261 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004262 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004263 if (kp->k_syn.id == id)
4264 {
4265 kp_next = kp->ke_next;
4266 if (kp_prev == NULL)
4267 {
4268 if (kp_next == NULL)
4269 hash_remove(ht, hi);
4270 else
4271 hi->hi_key = KE2HIKEY(kp_next);
4272 }
4273 else
4274 kp_prev->ke_next = kp_next;
4275 vim_free(kp->next_list);
4276 vim_free(kp->k_syn.cont_in_list);
4277 vim_free(kp);
4278 kp = kp_next;
4279 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004280 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004281 {
4282 kp_prev = kp;
4283 kp = kp->ke_next;
4284 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004285 }
4286 }
4287 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004288 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004289}
4290
4291/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004292 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004293 */
4294 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004295clear_keywtab(ht)
4296 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004297{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004298 hashitem_T *hi;
4299 int todo;
4300 keyentry_T *kp;
4301 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004302
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004303 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004304 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004305 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004306 if (!HASHITEM_EMPTY(hi))
4307 {
4308 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004309 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004310 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004311 kp_next = kp->ke_next;
4312 vim_free(kp->next_list);
4313 vim_free(kp->k_syn.cont_in_list);
4314 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004315 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004316 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004317 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004318 hash_clear(ht);
4319 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004320}
4321
4322/*
4323 * Add a keyword to the list of keywords.
4324 */
4325 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02004326add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004327 char_u *name; /* name of keyword */
4328 int id; /* group ID for this keyword */
4329 int flags; /* flags for this keyword */
4330 short *cont_in_list; /* containedin for this keyword */
4331 short *next_list; /* nextgroup for this keyword */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004332 int conceal_char;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004333{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004334 keyentry_T *kp;
4335 hashtab_T *ht;
4336 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004337 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004338 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004339 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004340
Bram Moolenaar860cae12010-06-05 23:22:07 +02004341 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004342 name_ic = str_foldcase(name, (int)STRLEN(name),
4343 name_folded, MAXKEYWLEN + 1);
4344 else
4345 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004346 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4347 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004348 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004349 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004350 kp->k_syn.id = id;
4351 kp->k_syn.inc_tag = current_syn_inc_tag;
4352 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004353 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004354 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004355 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004356 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004357 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004358
Bram Moolenaar860cae12010-06-05 23:22:07 +02004359 if (curwin->w_s->b_syn_ic)
4360 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004361 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004362 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004363
Bram Moolenaardad6b692005-01-25 22:14:34 +00004364 hash = hash_hash(kp->keyword);
4365 hi = hash_lookup(ht, kp->keyword, hash);
4366 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004367 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004368 /* new keyword, add to hashtable */
4369 kp->ke_next = NULL;
4370 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004371 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004372 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004373 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004374 /* keyword already exists, prepend to list */
4375 kp->ke_next = HI2KE(hi);
4376 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004377 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004378}
4379
4380/*
4381 * Get the start and end of the group name argument.
4382 * Return a pointer to the first argument.
4383 * Return NULL if the end of the command was found instead of further args.
4384 */
4385 static char_u *
4386get_group_name(arg, name_end)
4387 char_u *arg; /* start of the argument */
4388 char_u **name_end; /* pointer to end of the name */
4389{
4390 char_u *rest;
4391
4392 *name_end = skiptowhite(arg);
4393 rest = skipwhite(*name_end);
4394
4395 /*
4396 * Check if there are enough arguments. The first argument may be a
4397 * pattern, where '|' is allowed, so only check for NUL.
4398 */
4399 if (ends_excmd(*arg) || *rest == NUL)
4400 return NULL;
4401 return rest;
4402}
4403
4404/*
4405 * Check for syntax command option arguments.
4406 * This can be called at any place in the list of arguments, and just picks
4407 * out the arguments that are known. Can be called several times in a row to
4408 * collect all options in between other arguments.
4409 * Return a pointer to the next argument (which isn't an option).
4410 * Return NULL for any error;
4411 */
4412 static char_u *
Bram Moolenaar860cae12010-06-05 23:22:07 +02004413get_syn_options(arg, opt, conceal_char)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004414 char_u *arg; /* next argument to be checked */
4415 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004416 int *conceal_char UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004417{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004418 char_u *gname_start, *gname;
4419 int syn_id;
4420 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004421 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004422 int i;
4423 int fidx;
4424 static struct flag
4425 {
4426 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004427 int argtype;
4428 int flags;
4429 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4430 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4431 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4432 {"eExXtTeEnNdD", 0, HL_EXTEND},
4433 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4434 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4435 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4436 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4437 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4438 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4439 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4440 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4441 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004442 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4443 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4444 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004445 {"cCoOnNtTaAiInNsS", 1, 0},
4446 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4447 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004448 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004449 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004450
4451 if (arg == NULL) /* already detected error */
4452 return NULL;
4453
Bram Moolenaar860cae12010-06-05 23:22:07 +02004454#ifdef FEAT_CONCEAL
4455 if (curwin->w_s->b_syn_conceal)
4456 opt->flags |= HL_CONCEAL;
4457#endif
4458
Bram Moolenaar071d4272004-06-13 20:20:40 +00004459 for (;;)
4460 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004461 /*
4462 * This is used very often when a large number of keywords is defined.
4463 * Need to skip quickly when no option name is found.
4464 * Also avoid tolower(), it's slow.
4465 */
4466 if (strchr(first_letters, *arg) == NULL)
4467 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004468
4469 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4470 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004471 p = flagtab[fidx].name;
4472 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4473 if (arg[len] != p[i] && arg[len] != p[i + 1])
4474 break;
4475 if (p[i] == NUL && (vim_iswhite(arg[len])
4476 || (flagtab[fidx].argtype > 0
4477 ? arg[len] == '='
4478 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004479 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004480 if (opt->keyword
4481 && (flagtab[fidx].flags == HL_DISPLAY
4482 || flagtab[fidx].flags == HL_FOLD
4483 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004484 /* treat "display", "fold" and "extend" as a keyword */
4485 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004486 break;
4487 }
4488 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004489 if (fidx < 0) /* no match found */
4490 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004491
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004492 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004493 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004494 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004495 {
4496 EMSG(_("E395: contains argument not accepted here"));
4497 return NULL;
4498 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004499 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004500 return NULL;
4501 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004502 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004503 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004504#if 0 /* cannot happen */
4505 if (opt->cont_in_list == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004506 {
4507 EMSG(_("E396: containedin argument not accepted here"));
4508 return NULL;
4509 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004510#endif
4511 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004512 return NULL;
4513 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004514 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004515 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004516 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004517 return NULL;
4518 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004519 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4520 {
4521#ifdef FEAT_MBYTE
4522 /* cchar=? */
4523 if (has_mbyte)
4524 {
4525# ifdef FEAT_CONCEAL
4526 *conceal_char = mb_ptr2char(arg + 6);
4527# endif
4528 arg += mb_ptr2len(arg + 6) - 1;
4529 }
4530 else
4531#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004532 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004533#ifdef FEAT_CONCEAL
4534 *conceal_char = arg[6];
4535#else
4536 ;
4537#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004538 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004539 arg = skipwhite(arg + 7);
4540 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004541 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004542 {
4543 opt->flags |= flagtab[fidx].flags;
4544 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004545
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004546 if (flagtab[fidx].flags == HL_SYNC_HERE
4547 || flagtab[fidx].flags == HL_SYNC_THERE)
4548 {
4549 if (opt->sync_idx == NULL)
4550 {
4551 EMSG(_("E393: group[t]here not accepted here"));
4552 return NULL;
4553 }
4554 gname_start = arg;
4555 arg = skiptowhite(arg);
4556 if (gname_start == arg)
4557 return NULL;
4558 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4559 if (gname == NULL)
4560 return NULL;
4561 if (STRCMP(gname, "NONE") == 0)
4562 *opt->sync_idx = NONE_IDX;
4563 else
4564 {
4565 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004566 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4567 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4568 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004569 {
4570 *opt->sync_idx = i;
4571 break;
4572 }
4573 if (i < 0)
4574 {
4575 EMSG2(_("E394: Didn't find region item for %s"), gname);
4576 vim_free(gname);
4577 return NULL;
4578 }
4579 }
4580
4581 vim_free(gname);
4582 arg = skipwhite(arg);
4583 }
4584#ifdef FEAT_FOLDING
4585 else if (flagtab[fidx].flags == HL_FOLD
4586 && foldmethodIsSyntax(curwin))
4587 /* Need to update folds later. */
4588 foldUpdateAll(curwin);
4589#endif
4590 }
4591 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004592
4593 return arg;
4594}
4595
4596/*
4597 * Adjustments to syntax item when declared in a ":syn include"'d file.
4598 * Set the contained flag, and if the item is not already contained, add it
4599 * to the specified top-level group, if any.
4600 */
4601 static void
4602syn_incl_toplevel(id, flagsp)
4603 int id;
4604 int *flagsp;
4605{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004606 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004607 return;
4608 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004609 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004610 {
4611 /* We have to alloc this, because syn_combine_list() will free it. */
4612 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004613 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004614
4615 if (grp_list != NULL)
4616 {
4617 grp_list[0] = id;
4618 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004619 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004620 CLUSTER_ADD);
4621 }
4622 }
4623}
4624
4625/*
4626 * Handle ":syntax include [@{group-name}] filename" command.
4627 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004628 static void
4629syn_cmd_include(eap, syncing)
4630 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004631 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004632{
4633 char_u *arg = eap->arg;
4634 int sgl_id = 1;
4635 char_u *group_name_end;
4636 char_u *rest;
4637 char_u *errormsg = NULL;
4638 int prev_toplvl_grp;
4639 int prev_syn_inc_tag;
4640 int source = FALSE;
4641
4642 eap->nextcmd = find_nextcmd(arg);
4643 if (eap->skip)
4644 return;
4645
4646 if (arg[0] == '@')
4647 {
4648 ++arg;
4649 rest = get_group_name(arg, &group_name_end);
4650 if (rest == NULL)
4651 {
4652 EMSG((char_u *)_("E397: Filename required"));
4653 return;
4654 }
4655 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4656 /* separate_nextcmd() and expand_filename() depend on this */
4657 eap->arg = rest;
4658 }
4659
4660 /*
4661 * Everything that's left, up to the next command, should be the
4662 * filename to include.
4663 */
4664 eap->argt |= (XFILE | NOSPC);
4665 separate_nextcmd(eap);
4666 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4667 {
4668 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4669 * file. Need to expand the file name first. In other cases
4670 * ":runtime!" is used. */
4671 source = TRUE;
4672 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4673 {
4674 if (errormsg != NULL)
4675 EMSG(errormsg);
4676 return;
4677 }
4678 }
4679
4680 /*
4681 * Save and restore the existing top-level grouplist id and ":syn
4682 * include" tag around the actual inclusion.
4683 */
4684 prev_syn_inc_tag = current_syn_inc_tag;
4685 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004686 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4687 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004688 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4689 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004690 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004691 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004692 current_syn_inc_tag = prev_syn_inc_tag;
4693}
4694
4695/*
4696 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4697 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004698 static void
4699syn_cmd_keyword(eap, syncing)
4700 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004701 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004702{
4703 char_u *arg = eap->arg;
4704 char_u *group_name_end;
4705 int syn_id;
4706 char_u *rest;
4707 char_u *keyword_copy;
4708 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004709 char_u *kw;
4710 syn_opt_arg_T syn_opt_arg;
4711 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004712 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004713
4714 rest = get_group_name(arg, &group_name_end);
4715
4716 if (rest != NULL)
4717 {
4718 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4719
4720 /* allocate a buffer, for removing the backslashes in the keyword */
4721 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4722 if (keyword_copy != NULL)
4723 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004724 syn_opt_arg.flags = 0;
4725 syn_opt_arg.keyword = TRUE;
4726 syn_opt_arg.sync_idx = NULL;
4727 syn_opt_arg.has_cont_list = FALSE;
4728 syn_opt_arg.cont_in_list = NULL;
4729 syn_opt_arg.next_list = NULL;
4730
Bram Moolenaar071d4272004-06-13 20:20:40 +00004731 /*
4732 * The options given apply to ALL keywords, so all options must be
4733 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004734 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004735 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004736 cnt = 0;
4737 p = keyword_copy;
4738 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004739 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004740 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004741 if (rest == NULL || ends_excmd(*rest))
4742 break;
4743 /* Copy the keyword, removing backslashes, and add a NUL. */
4744 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004745 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004746 if (*rest == '\\' && rest[1] != NUL)
4747 ++rest;
4748 *p++ = *rest++;
4749 }
4750 *p++ = NUL;
4751 ++cnt;
4752 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004753
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004754 if (!eap->skip)
4755 {
4756 /* Adjust flags for use of ":syn include". */
4757 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4758
4759 /*
4760 * 2: Add an entry for each keyword.
4761 */
4762 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4763 {
4764 for (p = vim_strchr(kw, '['); ; )
4765 {
4766 if (p != NULL)
4767 *p = NUL;
4768 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004769 syn_opt_arg.cont_in_list,
4770 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004771 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004772 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004773 if (p[1] == NUL)
4774 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004775 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004776 kw = p + 2; /* skip over the NUL */
4777 break;
4778 }
4779 if (p[1] == ']')
4780 {
4781 kw = p + 1; /* skip over the "]" */
4782 break;
4783 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004784#ifdef FEAT_MBYTE
4785 if (has_mbyte)
4786 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004787 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004788
4789 mch_memmove(p, p + 1, l);
4790 p += l;
4791 }
4792 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004793#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004794 {
4795 p[0] = p[1];
4796 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004797 }
4798 }
4799 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004800 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004801
Bram Moolenaar071d4272004-06-13 20:20:40 +00004802 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004803 vim_free(syn_opt_arg.cont_in_list);
4804 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004805 }
4806 }
4807
4808 if (rest != NULL)
4809 eap->nextcmd = check_nextcmd(rest);
4810 else
4811 EMSG2(_(e_invarg2), arg);
4812
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004813 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004814 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004815}
4816
4817/*
4818 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4819 *
4820 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4821 */
4822 static void
4823syn_cmd_match(eap, syncing)
4824 exarg_T *eap;
4825 int syncing; /* TRUE for ":syntax sync match .. " */
4826{
4827 char_u *arg = eap->arg;
4828 char_u *group_name_end;
4829 char_u *rest;
4830 synpat_T item; /* the item found in the line */
4831 int syn_id;
4832 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004833 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004834 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004835 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004836
4837 /* Isolate the group name, check for validity */
4838 rest = get_group_name(arg, &group_name_end);
4839
4840 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004841 syn_opt_arg.flags = 0;
4842 syn_opt_arg.keyword = FALSE;
4843 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4844 syn_opt_arg.has_cont_list = TRUE;
4845 syn_opt_arg.cont_list = NULL;
4846 syn_opt_arg.cont_in_list = NULL;
4847 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004848 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004849
4850 /* get the pattern. */
4851 init_syn_patterns();
4852 vim_memset(&item, 0, sizeof(item));
4853 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004854 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4855 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004856
4857 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004858 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004859
4860 if (rest != NULL) /* all arguments are valid */
4861 {
4862 /*
4863 * Check for trailing command and illegal trailing arguments.
4864 */
4865 eap->nextcmd = check_nextcmd(rest);
4866 if (!ends_excmd(*rest) || eap->skip)
4867 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004868 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004869 && (syn_id = syn_check_group(arg,
4870 (int)(group_name_end - arg))) != 0)
4871 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004872 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004873 /*
4874 * Store the pattern in the syn_items list
4875 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004876 idx = curwin->w_s->b_syn_patterns.ga_len;
4877 SYN_ITEMS(curwin->w_s)[idx] = item;
4878 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4879 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4880 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4881 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4882 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4883 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4884 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4885 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004886 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004887#ifdef FEAT_CONCEAL
4888 SYN_ITEMS(curwin->w_s)[idx].sp_char = conceal_char;
4889#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004890 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004891 curwin->w_s->b_syn_containedin = TRUE;
4892 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4893 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004894
4895 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004896 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004897 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004898#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004899 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004900 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004901#endif
4902
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004903 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004904 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004905 return; /* don't free the progs and patterns now */
4906 }
4907 }
4908
4909 /*
4910 * Something failed, free the allocated memory.
4911 */
4912 vim_free(item.sp_prog);
4913 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004914 vim_free(syn_opt_arg.cont_list);
4915 vim_free(syn_opt_arg.cont_in_list);
4916 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004917
4918 if (rest == NULL)
4919 EMSG2(_(e_invarg2), arg);
4920}
4921
4922/*
4923 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4924 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4925 */
4926 static void
4927syn_cmd_region(eap, syncing)
4928 exarg_T *eap;
4929 int syncing; /* TRUE for ":syntax sync region .." */
4930{
4931 char_u *arg = eap->arg;
4932 char_u *group_name_end;
4933 char_u *rest; /* next arg, NULL on error */
4934 char_u *key_end;
4935 char_u *key = NULL;
4936 char_u *p;
4937 int item;
4938#define ITEM_START 0
4939#define ITEM_SKIP 1
4940#define ITEM_END 2
4941#define ITEM_MATCHGROUP 3
4942 struct pat_ptr
4943 {
4944 synpat_T *pp_synp; /* pointer to syn_pattern */
4945 int pp_matchgroup_id; /* matchgroup ID */
4946 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4947 } *(pat_ptrs[3]);
4948 /* patterns found in the line */
4949 struct pat_ptr *ppp;
4950 struct pat_ptr *ppp_next;
4951 int pat_count = 0; /* nr of syn_patterns found */
4952 int syn_id;
4953 int matchgroup_id = 0;
4954 int not_enough = FALSE; /* not enough arguments */
4955 int illegal = FALSE; /* illegal arguments */
4956 int success = FALSE;
4957 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004958 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004959 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004960
4961 /* Isolate the group name, check for validity */
4962 rest = get_group_name(arg, &group_name_end);
4963
4964 pat_ptrs[0] = NULL;
4965 pat_ptrs[1] = NULL;
4966 pat_ptrs[2] = NULL;
4967
4968 init_syn_patterns();
4969
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004970 syn_opt_arg.flags = 0;
4971 syn_opt_arg.keyword = FALSE;
4972 syn_opt_arg.sync_idx = NULL;
4973 syn_opt_arg.has_cont_list = TRUE;
4974 syn_opt_arg.cont_list = NULL;
4975 syn_opt_arg.cont_in_list = NULL;
4976 syn_opt_arg.next_list = NULL;
4977
Bram Moolenaar071d4272004-06-13 20:20:40 +00004978 /*
4979 * get the options, patterns and matchgroup.
4980 */
4981 while (rest != NULL && !ends_excmd(*rest))
4982 {
4983 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004984 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004985 if (rest == NULL || ends_excmd(*rest))
4986 break;
4987
4988 /* must be a pattern or matchgroup then */
4989 key_end = rest;
4990 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4991 ++key_end;
4992 vim_free(key);
4993 key = vim_strnsave_up(rest, (int)(key_end - rest));
4994 if (key == NULL) /* out of memory */
4995 {
4996 rest = NULL;
4997 break;
4998 }
4999 if (STRCMP(key, "MATCHGROUP") == 0)
5000 item = ITEM_MATCHGROUP;
5001 else if (STRCMP(key, "START") == 0)
5002 item = ITEM_START;
5003 else if (STRCMP(key, "END") == 0)
5004 item = ITEM_END;
5005 else if (STRCMP(key, "SKIP") == 0)
5006 {
5007 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5008 {
5009 illegal = TRUE;
5010 break;
5011 }
5012 item = ITEM_SKIP;
5013 }
5014 else
5015 break;
5016 rest = skipwhite(key_end);
5017 if (*rest != '=')
5018 {
5019 rest = NULL;
5020 EMSG2(_("E398: Missing '=': %s"), arg);
5021 break;
5022 }
5023 rest = skipwhite(rest + 1);
5024 if (*rest == NUL)
5025 {
5026 not_enough = TRUE;
5027 break;
5028 }
5029
5030 if (item == ITEM_MATCHGROUP)
5031 {
5032 p = skiptowhite(rest);
5033 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5034 matchgroup_id = 0;
5035 else
5036 {
5037 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5038 if (matchgroup_id == 0)
5039 {
5040 illegal = TRUE;
5041 break;
5042 }
5043 }
5044 rest = skipwhite(p);
5045 }
5046 else
5047 {
5048 /*
5049 * Allocate room for a syn_pattern, and link it in the list of
5050 * syn_patterns for this item, at the start (because the list is
5051 * used from end to start).
5052 */
5053 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5054 if (ppp == NULL)
5055 {
5056 rest = NULL;
5057 break;
5058 }
5059 ppp->pp_next = pat_ptrs[item];
5060 pat_ptrs[item] = ppp;
5061 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5062 if (ppp->pp_synp == NULL)
5063 {
5064 rest = NULL;
5065 break;
5066 }
5067
5068 /*
5069 * Get the syntax pattern and the following offset(s).
5070 */
5071 /* Enable the appropriate \z specials. */
5072 if (item == ITEM_START)
5073 reg_do_extmatch = REX_SET;
5074 else if (item == ITEM_SKIP || item == ITEM_END)
5075 reg_do_extmatch = REX_USE;
5076 rest = get_syn_pattern(rest, ppp->pp_synp);
5077 reg_do_extmatch = 0;
5078 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005079 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005080 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5081 ppp->pp_matchgroup_id = matchgroup_id;
5082 ++pat_count;
5083 }
5084 }
5085 vim_free(key);
5086 if (illegal || not_enough)
5087 rest = NULL;
5088
5089 /*
5090 * Must have a "start" and "end" pattern.
5091 */
5092 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5093 pat_ptrs[ITEM_END] == NULL))
5094 {
5095 not_enough = TRUE;
5096 rest = NULL;
5097 }
5098
5099 if (rest != NULL)
5100 {
5101 /*
5102 * Check for trailing garbage or command.
5103 * If OK, add the item.
5104 */
5105 eap->nextcmd = check_nextcmd(rest);
5106 if (!ends_excmd(*rest) || eap->skip)
5107 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005108 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005109 && (syn_id = syn_check_group(arg,
5110 (int)(group_name_end - arg))) != 0)
5111 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005112 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005113 /*
5114 * Store the start/skip/end in the syn_items list
5115 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005116 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005117 for (item = ITEM_START; item <= ITEM_END; ++item)
5118 {
5119 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5120 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005121 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5122 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5123 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005124 (item == ITEM_START) ? SPTYPE_START :
5125 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005126 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5127 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5128 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5129 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005130 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005131#ifdef FEAT_CONCEAL
5132 SYN_ITEMS(curwin->w_s)[idx].sp_char = conceal_char;
5133#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005134 if (item == ITEM_START)
5135 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005136 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005137 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005138 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005139 syn_opt_arg.cont_in_list;
5140 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005141 curwin->w_s->b_syn_containedin = TRUE;
5142 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005143 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005144 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005145 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005146 ++idx;
5147#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005148 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005149 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005150#endif
5151 }
5152 }
5153
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005154 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005155 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005156 success = TRUE; /* don't free the progs and patterns now */
5157 }
5158 }
5159
5160 /*
5161 * Free the allocated memory.
5162 */
5163 for (item = ITEM_START; item <= ITEM_END; ++item)
5164 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5165 {
5166 if (!success)
5167 {
5168 vim_free(ppp->pp_synp->sp_prog);
5169 vim_free(ppp->pp_synp->sp_pattern);
5170 }
5171 vim_free(ppp->pp_synp);
5172 ppp_next = ppp->pp_next;
5173 vim_free(ppp);
5174 }
5175
5176 if (!success)
5177 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005178 vim_free(syn_opt_arg.cont_list);
5179 vim_free(syn_opt_arg.cont_in_list);
5180 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005181 if (not_enough)
5182 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5183 else if (illegal || rest == NULL)
5184 EMSG2(_(e_invarg2), arg);
5185 }
5186}
5187
5188/*
5189 * A simple syntax group ID comparison function suitable for use in qsort()
5190 */
5191 static int
5192#ifdef __BORLANDC__
5193_RTLENTRYF
5194#endif
5195syn_compare_stub(v1, v2)
5196 const void *v1;
5197 const void *v2;
5198{
5199 const short *s1 = v1;
5200 const short *s2 = v2;
5201
5202 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5203}
5204
5205/*
5206 * Combines lists of syntax clusters.
5207 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5208 */
5209 static void
5210syn_combine_list(clstr1, clstr2, list_op)
5211 short **clstr1;
5212 short **clstr2;
5213 int list_op;
5214{
5215 int count1 = 0;
5216 int count2 = 0;
5217 short *g1;
5218 short *g2;
5219 short *clstr = NULL;
5220 int count;
5221 int round;
5222
5223 /*
5224 * Handle degenerate cases.
5225 */
5226 if (*clstr2 == NULL)
5227 return;
5228 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5229 {
5230 if (list_op == CLUSTER_REPLACE)
5231 vim_free(*clstr1);
5232 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5233 *clstr1 = *clstr2;
5234 else
5235 vim_free(*clstr2);
5236 return;
5237 }
5238
5239 for (g1 = *clstr1; *g1; g1++)
5240 ++count1;
5241 for (g2 = *clstr2; *g2; g2++)
5242 ++count2;
5243
5244 /*
5245 * For speed purposes, sort both lists.
5246 */
5247 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5248 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5249
5250 /*
5251 * We proceed in two passes; in round 1, we count the elements to place
5252 * in the new list, and in round 2, we allocate and populate the new
5253 * list. For speed, we use a mergesort-like method, adding the smaller
5254 * of the current elements in each list to the new list.
5255 */
5256 for (round = 1; round <= 2; round++)
5257 {
5258 g1 = *clstr1;
5259 g2 = *clstr2;
5260 count = 0;
5261
5262 /*
5263 * First, loop through the lists until one of them is empty.
5264 */
5265 while (*g1 && *g2)
5266 {
5267 /*
5268 * We always want to add from the first list.
5269 */
5270 if (*g1 < *g2)
5271 {
5272 if (round == 2)
5273 clstr[count] = *g1;
5274 count++;
5275 g1++;
5276 continue;
5277 }
5278 /*
5279 * We only want to add from the second list if we're adding the
5280 * lists.
5281 */
5282 if (list_op == CLUSTER_ADD)
5283 {
5284 if (round == 2)
5285 clstr[count] = *g2;
5286 count++;
5287 }
5288 if (*g1 == *g2)
5289 g1++;
5290 g2++;
5291 }
5292
5293 /*
5294 * Now add the leftovers from whichever list didn't get finished
5295 * first. As before, we only want to add from the second list if
5296 * we're adding the lists.
5297 */
5298 for (; *g1; g1++, count++)
5299 if (round == 2)
5300 clstr[count] = *g1;
5301 if (list_op == CLUSTER_ADD)
5302 for (; *g2; g2++, count++)
5303 if (round == 2)
5304 clstr[count] = *g2;
5305
5306 if (round == 1)
5307 {
5308 /*
5309 * If the group ended up empty, we don't need to allocate any
5310 * space for it.
5311 */
5312 if (count == 0)
5313 {
5314 clstr = NULL;
5315 break;
5316 }
5317 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5318 if (clstr == NULL)
5319 break;
5320 clstr[count] = 0;
5321 }
5322 }
5323
5324 /*
5325 * Finally, put the new list in place.
5326 */
5327 vim_free(*clstr1);
5328 vim_free(*clstr2);
5329 *clstr1 = clstr;
5330}
5331
5332/*
5333 * Lookup a syntax cluster name and return it's ID.
5334 * If it is not found, 0 is returned.
5335 */
5336 static int
5337syn_scl_name2id(name)
5338 char_u *name;
5339{
5340 int i;
5341 char_u *name_u;
5342
5343 /* Avoid using stricmp() too much, it's slow on some systems */
5344 name_u = vim_strsave_up(name);
5345 if (name_u == NULL)
5346 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005347 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5348 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5349 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005350 break;
5351 vim_free(name_u);
5352 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5353}
5354
5355/*
5356 * Like syn_scl_name2id(), but take a pointer + length argument.
5357 */
5358 static int
5359syn_scl_namen2id(linep, len)
5360 char_u *linep;
5361 int len;
5362{
5363 char_u *name;
5364 int id = 0;
5365
5366 name = vim_strnsave(linep, len);
5367 if (name != NULL)
5368 {
5369 id = syn_scl_name2id(name);
5370 vim_free(name);
5371 }
5372 return id;
5373}
5374
5375/*
5376 * Find syntax cluster name in the table and return it's ID.
5377 * The argument is a pointer to the name and the length of the name.
5378 * If it doesn't exist yet, a new entry is created.
5379 * Return 0 for failure.
5380 */
5381 static int
5382syn_check_cluster(pp, len)
5383 char_u *pp;
5384 int len;
5385{
5386 int id;
5387 char_u *name;
5388
5389 name = vim_strnsave(pp, len);
5390 if (name == NULL)
5391 return 0;
5392
5393 id = syn_scl_name2id(name);
5394 if (id == 0) /* doesn't exist yet */
5395 id = syn_add_cluster(name);
5396 else
5397 vim_free(name);
5398 return id;
5399}
5400
5401/*
5402 * Add new syntax cluster and return it's ID.
5403 * "name" must be an allocated string, it will be consumed.
5404 * Return 0 for failure.
5405 */
5406 static int
5407syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005408 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005409{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005410 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005411
5412 /*
5413 * First call for this growarray: init growing array.
5414 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005415 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005416 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005417 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5418 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005419 }
5420
5421 /*
5422 * Make room for at least one other cluster entry.
5423 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005424 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005425 {
5426 vim_free(name);
5427 return 0;
5428 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005429 len = curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005430
Bram Moolenaar860cae12010-06-05 23:22:07 +02005431 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5432 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5433 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5434 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5435 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005436
Bram Moolenaar217ad922005-03-20 22:37:15 +00005437 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005438 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005439 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005440 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005441
Bram Moolenaar071d4272004-06-13 20:20:40 +00005442 return len + SYNID_CLUSTER;
5443}
5444
5445/*
5446 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5447 * [add={groupname},..] [remove={groupname},..]".
5448 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005449 static void
5450syn_cmd_cluster(eap, syncing)
5451 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005452 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005453{
5454 char_u *arg = eap->arg;
5455 char_u *group_name_end;
5456 char_u *rest;
5457 int scl_id;
5458 short *clstr_list;
5459 int got_clstr = FALSE;
5460 int opt_len;
5461 int list_op;
5462
5463 eap->nextcmd = find_nextcmd(arg);
5464 if (eap->skip)
5465 return;
5466
5467 rest = get_group_name(arg, &group_name_end);
5468
5469 if (rest != NULL)
5470 {
5471 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
Bram Moolenaar217ad922005-03-20 22:37:15 +00005472 - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005473
5474 for (;;)
5475 {
5476 if (STRNICMP(rest, "add", 3) == 0
5477 && (vim_iswhite(rest[3]) || rest[3] == '='))
5478 {
5479 opt_len = 3;
5480 list_op = CLUSTER_ADD;
5481 }
5482 else if (STRNICMP(rest, "remove", 6) == 0
5483 && (vim_iswhite(rest[6]) || rest[6] == '='))
5484 {
5485 opt_len = 6;
5486 list_op = CLUSTER_SUBTRACT;
5487 }
5488 else if (STRNICMP(rest, "contains", 8) == 0
5489 && (vim_iswhite(rest[8]) || rest[8] == '='))
5490 {
5491 opt_len = 8;
5492 list_op = CLUSTER_REPLACE;
5493 }
5494 else
5495 break;
5496
5497 clstr_list = NULL;
5498 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5499 {
5500 EMSG2(_(e_invarg2), rest);
5501 break;
5502 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005503 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005504 &clstr_list, list_op);
5505 got_clstr = TRUE;
5506 }
5507
5508 if (got_clstr)
5509 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005510 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005511 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005512 }
5513 }
5514
5515 if (!got_clstr)
5516 EMSG(_("E400: No cluster specified"));
5517 if (rest == NULL || !ends_excmd(*rest))
5518 EMSG2(_(e_invarg2), arg);
5519}
5520
5521/*
5522 * On first call for current buffer: Init growing array.
5523 */
5524 static void
5525init_syn_patterns()
5526{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005527 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5528 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005529}
5530
5531/*
5532 * Get one pattern for a ":syntax match" or ":syntax region" command.
5533 * Stores the pattern and program in a synpat_T.
5534 * Returns a pointer to the next argument, or NULL in case of an error.
5535 */
5536 static char_u *
5537get_syn_pattern(arg, ci)
5538 char_u *arg;
5539 synpat_T *ci;
5540{
5541 char_u *end;
5542 int *p;
5543 int idx;
5544 char_u *cpo_save;
5545
5546 /* need at least three chars */
5547 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5548 return NULL;
5549
5550 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5551 if (*end != *arg) /* end delimiter not found */
5552 {
5553 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5554 return NULL;
5555 }
5556 /* store the pattern and compiled regexp program */
5557 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5558 return NULL;
5559
5560 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5561 cpo_save = p_cpo;
5562 p_cpo = (char_u *)"";
5563 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5564 p_cpo = cpo_save;
5565
5566 if (ci->sp_prog == NULL)
5567 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005568 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005569
5570 /*
5571 * Check for a match, highlight or region offset.
5572 */
5573 ++end;
5574 do
5575 {
5576 for (idx = SPO_COUNT; --idx >= 0; )
5577 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5578 break;
5579 if (idx >= 0)
5580 {
5581 p = &(ci->sp_offsets[idx]);
5582 if (idx != SPO_LC_OFF)
5583 switch (end[3])
5584 {
5585 case 's': break;
5586 case 'b': break;
5587 case 'e': idx += SPO_COUNT; break;
5588 default: idx = -1; break;
5589 }
5590 if (idx >= 0)
5591 {
5592 ci->sp_off_flags |= (1 << idx);
5593 if (idx == SPO_LC_OFF) /* lc=99 */
5594 {
5595 end += 3;
5596 *p = getdigits(&end);
5597
5598 /* "lc=" offset automatically sets "ms=" offset */
5599 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5600 {
5601 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5602 ci->sp_offsets[SPO_MS_OFF] = *p;
5603 }
5604 }
5605 else /* yy=x+99 */
5606 {
5607 end += 4;
5608 if (*end == '+')
5609 {
5610 ++end;
5611 *p = getdigits(&end); /* positive offset */
5612 }
5613 else if (*end == '-')
5614 {
5615 ++end;
5616 *p = -getdigits(&end); /* negative offset */
5617 }
5618 }
5619 if (*end != ',')
5620 break;
5621 ++end;
5622 }
5623 }
5624 } while (idx >= 0);
5625
5626 if (!ends_excmd(*end) && !vim_iswhite(*end))
5627 {
5628 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5629 return NULL;
5630 }
5631 return skipwhite(end);
5632}
5633
5634/*
5635 * Handle ":syntax sync .." command.
5636 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005637 static void
5638syn_cmd_sync(eap, syncing)
5639 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005640 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005641{
5642 char_u *arg_start = eap->arg;
5643 char_u *arg_end;
5644 char_u *key = NULL;
5645 char_u *next_arg;
5646 int illegal = FALSE;
5647 int finished = FALSE;
5648 long n;
5649 char_u *cpo_save;
5650
5651 if (ends_excmd(*arg_start))
5652 {
5653 syn_cmd_list(eap, TRUE);
5654 return;
5655 }
5656
5657 while (!ends_excmd(*arg_start))
5658 {
5659 arg_end = skiptowhite(arg_start);
5660 next_arg = skipwhite(arg_end);
5661 vim_free(key);
5662 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5663 if (STRCMP(key, "CCOMMENT") == 0)
5664 {
5665 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005666 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005667 if (!ends_excmd(*next_arg))
5668 {
5669 arg_end = skiptowhite(next_arg);
5670 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005671 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005672 (int)(arg_end - next_arg));
5673 next_arg = skipwhite(arg_end);
5674 }
5675 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005676 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005677 }
5678 else if ( STRNCMP(key, "LINES", 5) == 0
5679 || STRNCMP(key, "MINLINES", 8) == 0
5680 || STRNCMP(key, "MAXLINES", 8) == 0
5681 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5682 {
5683 if (key[4] == 'S')
5684 arg_end = key + 6;
5685 else if (key[0] == 'L')
5686 arg_end = key + 11;
5687 else
5688 arg_end = key + 9;
5689 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5690 {
5691 illegal = TRUE;
5692 break;
5693 }
5694 n = getdigits(&arg_end);
5695 if (!eap->skip)
5696 {
5697 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005698 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005699 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005700 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005701 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005702 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005703 }
5704 }
5705 else if (STRCMP(key, "FROMSTART") == 0)
5706 {
5707 if (!eap->skip)
5708 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005709 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5710 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005711 }
5712 }
5713 else if (STRCMP(key, "LINECONT") == 0)
5714 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005715 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005716 {
5717 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5718 finished = TRUE;
5719 break;
5720 }
5721 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5722 if (*arg_end != *next_arg) /* end delimiter not found */
5723 {
5724 illegal = TRUE;
5725 break;
5726 }
5727
5728 if (!eap->skip)
5729 {
5730 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005731 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005732 (int)(arg_end - next_arg - 1))) == NULL)
5733 {
5734 finished = TRUE;
5735 break;
5736 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005737 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005738
5739 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5740 cpo_save = p_cpo;
5741 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005742 curwin->w_s->b_syn_linecont_prog =
5743 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005744 p_cpo = cpo_save;
5745
Bram Moolenaar860cae12010-06-05 23:22:07 +02005746 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005747 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005748 vim_free(curwin->w_s->b_syn_linecont_pat);
5749 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005750 finished = TRUE;
5751 break;
5752 }
5753 }
5754 next_arg = skipwhite(arg_end + 1);
5755 }
5756 else
5757 {
5758 eap->arg = next_arg;
5759 if (STRCMP(key, "MATCH") == 0)
5760 syn_cmd_match(eap, TRUE);
5761 else if (STRCMP(key, "REGION") == 0)
5762 syn_cmd_region(eap, TRUE);
5763 else if (STRCMP(key, "CLEAR") == 0)
5764 syn_cmd_clear(eap, TRUE);
5765 else
5766 illegal = TRUE;
5767 finished = TRUE;
5768 break;
5769 }
5770 arg_start = next_arg;
5771 }
5772 vim_free(key);
5773 if (illegal)
5774 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5775 else if (!finished)
5776 {
5777 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005778 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005779 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005780 }
5781}
5782
5783/*
5784 * Convert a line of highlight group names into a list of group ID numbers.
5785 * "arg" should point to the "contains" or "nextgroup" keyword.
5786 * "arg" is advanced to after the last group name.
5787 * Careful: the argument is modified (NULs added).
5788 * returns FAIL for some error, OK for success.
5789 */
5790 static int
5791get_id_list(arg, keylen, list)
5792 char_u **arg;
5793 int keylen; /* length of keyword */
5794 short **list; /* where to store the resulting list, if not
5795 NULL, the list is silently skipped! */
5796{
5797 char_u *p = NULL;
5798 char_u *end;
5799 int round;
5800 int count;
5801 int total_count = 0;
5802 short *retval = NULL;
5803 char_u *name;
5804 regmatch_T regmatch;
5805 int id;
5806 int i;
5807 int failed = FALSE;
5808
5809 /*
5810 * We parse the list twice:
5811 * round == 1: count the number of items, allocate the array.
5812 * round == 2: fill the array with the items.
5813 * In round 1 new groups may be added, causing the number of items to
5814 * grow when a regexp is used. In that case round 1 is done once again.
5815 */
5816 for (round = 1; round <= 2; ++round)
5817 {
5818 /*
5819 * skip "contains"
5820 */
5821 p = skipwhite(*arg + keylen);
5822 if (*p != '=')
5823 {
5824 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5825 break;
5826 }
5827 p = skipwhite(p + 1);
5828 if (ends_excmd(*p))
5829 {
5830 EMSG2(_("E406: Empty argument: %s"), *arg);
5831 break;
5832 }
5833
5834 /*
5835 * parse the arguments after "contains"
5836 */
5837 count = 0;
5838 while (!ends_excmd(*p))
5839 {
5840 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5841 ;
5842 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5843 if (name == NULL)
5844 {
5845 failed = TRUE;
5846 break;
5847 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005848 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005849 if ( STRCMP(name + 1, "ALLBUT") == 0
5850 || STRCMP(name + 1, "ALL") == 0
5851 || STRCMP(name + 1, "TOP") == 0
5852 || STRCMP(name + 1, "CONTAINED") == 0)
5853 {
5854 if (TOUPPER_ASC(**arg) != 'C')
5855 {
5856 EMSG2(_("E407: %s not allowed here"), name + 1);
5857 failed = TRUE;
5858 vim_free(name);
5859 break;
5860 }
5861 if (count != 0)
5862 {
5863 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5864 failed = TRUE;
5865 vim_free(name);
5866 break;
5867 }
5868 if (name[1] == 'A')
5869 id = SYNID_ALLBUT;
5870 else if (name[1] == 'T')
5871 id = SYNID_TOP;
5872 else
5873 id = SYNID_CONTAINED;
5874 id += current_syn_inc_tag;
5875 }
5876 else if (name[1] == '@')
5877 {
5878 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5879 }
5880 else
5881 {
5882 /*
5883 * Handle full group name.
5884 */
5885 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5886 id = syn_check_group(name + 1, (int)(end - p));
5887 else
5888 {
5889 /*
5890 * Handle match of regexp with group names.
5891 */
5892 *name = '^';
5893 STRCAT(name, "$");
5894 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5895 if (regmatch.regprog == NULL)
5896 {
5897 failed = TRUE;
5898 vim_free(name);
5899 break;
5900 }
5901
5902 regmatch.rm_ic = TRUE;
5903 id = 0;
5904 for (i = highlight_ga.ga_len; --i >= 0; )
5905 {
5906 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5907 (colnr_T)0))
5908 {
5909 if (round == 2)
5910 {
5911 /* Got more items than expected; can happen
5912 * when adding items that match:
5913 * "contains=a.*b,axb".
5914 * Go back to first round */
5915 if (count >= total_count)
5916 {
5917 vim_free(retval);
5918 round = 1;
5919 }
5920 else
5921 retval[count] = i + 1;
5922 }
5923 ++count;
5924 id = -1; /* remember that we found one */
5925 }
5926 }
5927 vim_free(regmatch.regprog);
5928 }
5929 }
5930 vim_free(name);
5931 if (id == 0)
5932 {
5933 EMSG2(_("E409: Unknown group name: %s"), p);
5934 failed = TRUE;
5935 break;
5936 }
5937 if (id > 0)
5938 {
5939 if (round == 2)
5940 {
5941 /* Got more items than expected, go back to first round */
5942 if (count >= total_count)
5943 {
5944 vim_free(retval);
5945 round = 1;
5946 }
5947 else
5948 retval[count] = id;
5949 }
5950 ++count;
5951 }
5952 p = skipwhite(end);
5953 if (*p != ',')
5954 break;
5955 p = skipwhite(p + 1); /* skip comma in between arguments */
5956 }
5957 if (failed)
5958 break;
5959 if (round == 1)
5960 {
5961 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5962 if (retval == NULL)
5963 break;
5964 retval[count] = 0; /* zero means end of the list */
5965 total_count = count;
5966 }
5967 }
5968
5969 *arg = p;
5970 if (failed || retval == NULL)
5971 {
5972 vim_free(retval);
5973 return FAIL;
5974 }
5975
5976 if (*list == NULL)
5977 *list = retval;
5978 else
5979 vim_free(retval); /* list already found, don't overwrite it */
5980
5981 return OK;
5982}
5983
5984/*
5985 * Make a copy of an ID list.
5986 */
5987 static short *
5988copy_id_list(list)
5989 short *list;
5990{
5991 int len;
5992 int count;
5993 short *retval;
5994
5995 if (list == NULL)
5996 return NULL;
5997
5998 for (count = 0; list[count]; ++count)
5999 ;
6000 len = (count + 1) * sizeof(short);
6001 retval = (short *)alloc((unsigned)len);
6002 if (retval != NULL)
6003 mch_memmove(retval, list, (size_t)len);
6004
6005 return retval;
6006}
6007
6008/*
6009 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6010 * "cur_si" can be NULL if not checking the "containedin" list.
6011 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6012 * the current item.
6013 * This function is called very often, keep it fast!!
6014 */
6015 static int
6016in_id_list(cur_si, list, ssp, contained)
6017 stateitem_T *cur_si; /* current item or NULL */
6018 short *list; /* id list */
6019 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
6020 int contained; /* group id is contained */
6021{
6022 int retval;
6023 short *scl_list;
6024 short item;
6025 short id = ssp->id;
6026 static int depth = 0;
6027 int r;
6028
6029 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006030 if (cur_si != NULL && ssp->cont_in_list != NULL
6031 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006032 {
6033 /* Ignore transparent items without a contains argument. Double check
6034 * that we don't go back past the first one. */
6035 while ((cur_si->si_flags & HL_TRANS_CONT)
6036 && cur_si > (stateitem_T *)(current_state.ga_data))
6037 --cur_si;
6038 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6039 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006040 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6041 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006042 return TRUE;
6043 }
6044
6045 if (list == NULL)
6046 return FALSE;
6047
6048 /*
6049 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6050 * inside anything. Only allow not-contained groups.
6051 */
6052 if (list == ID_LIST_ALL)
6053 return !contained;
6054
6055 /*
6056 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6057 * contains list. We also require that "id" is at the same ":syn include"
6058 * level as the list.
6059 */
6060 item = *list;
6061 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6062 {
6063 if (item < SYNID_TOP)
6064 {
6065 /* ALL or ALLBUT: accept all groups in the same file */
6066 if (item - SYNID_ALLBUT != ssp->inc_tag)
6067 return FALSE;
6068 }
6069 else if (item < SYNID_CONTAINED)
6070 {
6071 /* TOP: accept all not-contained groups in the same file */
6072 if (item - SYNID_TOP != ssp->inc_tag || contained)
6073 return FALSE;
6074 }
6075 else
6076 {
6077 /* CONTAINED: accept all contained groups in the same file */
6078 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6079 return FALSE;
6080 }
6081 item = *++list;
6082 retval = FALSE;
6083 }
6084 else
6085 retval = TRUE;
6086
6087 /*
6088 * Return "retval" if id is in the contains list.
6089 */
6090 while (item != 0)
6091 {
6092 if (item == id)
6093 return retval;
6094 if (item >= SYNID_CLUSTER)
6095 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006096 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006097 /* restrict recursiveness to 30 to avoid an endless loop for a
6098 * cluster that includes itself (indirectly) */
6099 if (scl_list != NULL && depth < 30)
6100 {
6101 ++depth;
6102 r = in_id_list(NULL, scl_list, ssp, contained);
6103 --depth;
6104 if (r)
6105 return retval;
6106 }
6107 }
6108 item = *++list;
6109 }
6110 return !retval;
6111}
6112
6113struct subcommand
6114{
6115 char *name; /* subcommand name */
6116 void (*func)__ARGS((exarg_T *, int)); /* function to call */
6117};
6118
6119static struct subcommand subcommands[] =
6120{
6121 {"case", syn_cmd_case},
6122 {"clear", syn_cmd_clear},
6123 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006124 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006125 {"enable", syn_cmd_enable},
6126 {"include", syn_cmd_include},
6127 {"keyword", syn_cmd_keyword},
6128 {"list", syn_cmd_list},
6129 {"manual", syn_cmd_manual},
6130 {"match", syn_cmd_match},
6131 {"on", syn_cmd_on},
6132 {"off", syn_cmd_off},
6133 {"region", syn_cmd_region},
6134 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006135 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006136 {"sync", syn_cmd_sync},
6137 {"", syn_cmd_list},
6138 {NULL, NULL}
6139};
6140
6141/*
6142 * ":syntax".
6143 * This searches the subcommands[] table for the subcommand name, and calls a
6144 * syntax_subcommand() function to do the rest.
6145 */
6146 void
6147ex_syntax(eap)
6148 exarg_T *eap;
6149{
6150 char_u *arg = eap->arg;
6151 char_u *subcmd_end;
6152 char_u *subcmd_name;
6153 int i;
6154
6155 syn_cmdlinep = eap->cmdlinep;
6156
6157 /* isolate subcommand name */
6158 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6159 ;
6160 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6161 if (subcmd_name != NULL)
6162 {
6163 if (eap->skip) /* skip error messages for all subcommands */
6164 ++emsg_skip;
6165 for (i = 0; ; ++i)
6166 {
6167 if (subcommands[i].name == NULL)
6168 {
6169 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6170 break;
6171 }
6172 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6173 {
6174 eap->arg = skipwhite(subcmd_end);
6175 (subcommands[i].func)(eap, FALSE);
6176 break;
6177 }
6178 }
6179 vim_free(subcmd_name);
6180 if (eap->skip)
6181 --emsg_skip;
6182 }
6183}
6184
Bram Moolenaar860cae12010-06-05 23:22:07 +02006185 void
6186ex_ownsyntax(eap)
6187 exarg_T *eap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006188{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006189 char_u *old_value;
6190 char_u *new_value;
6191
Bram Moolenaar860cae12010-06-05 23:22:07 +02006192 if (curwin->w_s == &curwin->w_buffer->b_s)
6193 {
6194 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6195 memset(curwin->w_s, 0, sizeof(synblock_T));
6196#ifdef FEAT_SPELL
6197 curwin->w_p_spell = FALSE; /* No spell checking */
6198 clear_string_option(&curwin->w_s->b_p_spc);
6199 clear_string_option(&curwin->w_s->b_p_spf);
6200 vim_free(curwin->w_s->b_cap_prog);
6201 curwin->w_s->b_cap_prog = NULL;
6202 clear_string_option(&curwin->w_s->b_p_spl);
6203#endif
6204 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006205
6206 /* save value of b:current_syntax */
6207 old_value = get_var_value((char_u *)"b:current_syntax");
6208 if (old_value != NULL)
6209 old_value = vim_strsave(old_value);
6210
6211 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6212 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006213 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006214
6215 /* move value of b:current_syntax to w:current_syntax */
6216 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006217 if (new_value != NULL)
6218 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006219
6220 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006221 if (old_value == NULL)
6222 do_unlet((char_u *)"b:current_syntax", TRUE);
6223 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006224 {
6225 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6226 vim_free(old_value);
6227 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006228}
6229
6230 int
6231syntax_present(win)
6232 win_T *win;
6233{
6234 return (win->w_s->b_syn_patterns.ga_len != 0
6235 || win->w_s->b_syn_clusters.ga_len != 0
6236 || win->w_s->b_keywtab.ht_used > 0
6237 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006238}
6239
6240#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6241
6242static enum
6243{
6244 EXP_SUBCMD, /* expand ":syn" sub-commands */
6245 EXP_CASE /* expand ":syn case" arguments */
6246} expand_what;
6247
Bram Moolenaar4f688582007-07-24 12:34:30 +00006248/*
6249 * Reset include_link, include_default, include_none to 0.
6250 * Called when we are done expanding.
6251 */
6252 void
6253reset_expand_highlight()
6254{
6255 include_link = include_default = include_none = 0;
6256}
6257
6258/*
6259 * Handle command line completion for :match and :echohl command: Add "None"
6260 * as highlight group.
6261 */
6262 void
6263set_context_in_echohl_cmd(xp, arg)
6264 expand_T *xp;
6265 char_u *arg;
6266{
6267 xp->xp_context = EXPAND_HIGHLIGHT;
6268 xp->xp_pattern = arg;
6269 include_none = 1;
6270}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006271
6272/*
6273 * Handle command line completion for :syntax command.
6274 */
6275 void
6276set_context_in_syntax_cmd(xp, arg)
6277 expand_T *xp;
6278 char_u *arg;
6279{
6280 char_u *p;
6281
6282 /* Default: expand subcommands */
6283 xp->xp_context = EXPAND_SYNTAX;
6284 expand_what = EXP_SUBCMD;
6285 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006286 include_link = 0;
6287 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006288
6289 /* (part of) subcommand already typed */
6290 if (*arg != NUL)
6291 {
6292 p = skiptowhite(arg);
6293 if (*p != NUL) /* past first word */
6294 {
6295 xp->xp_pattern = skipwhite(p);
6296 if (*skiptowhite(xp->xp_pattern) != NUL)
6297 xp->xp_context = EXPAND_NOTHING;
6298 else if (STRNICMP(arg, "case", p - arg) == 0)
6299 expand_what = EXP_CASE;
6300 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6301 || STRNICMP(arg, "region", p - arg) == 0
6302 || STRNICMP(arg, "match", p - arg) == 0
6303 || STRNICMP(arg, "list", p - arg) == 0)
6304 xp->xp_context = EXPAND_HIGHLIGHT;
6305 else
6306 xp->xp_context = EXPAND_NOTHING;
6307 }
6308 }
6309}
6310
6311static char *(case_args[]) = {"match", "ignore", NULL};
6312
6313/*
6314 * Function given to ExpandGeneric() to obtain the list syntax names for
6315 * expansion.
6316 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006317 char_u *
6318get_syntax_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00006319 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006320 int idx;
6321{
6322 if (expand_what == EXP_SUBCMD)
6323 return (char_u *)subcommands[idx].name;
6324 return (char_u *)case_args[idx];
6325}
6326
6327#endif /* FEAT_CMDL_COMPL */
6328
Bram Moolenaar071d4272004-06-13 20:20:40 +00006329/*
6330 * Function called for expression evaluation: get syntax ID at file position.
6331 */
6332 int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006333syn_get_id(wp, lnum, col, trans, spellp, keep_state)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006334 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006335 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006336 colnr_T col;
Bram Moolenaarf5b63862009-12-16 17:13:44 +00006337 int trans; /* remove transparency */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006338 int *spellp; /* return: can do spell checking */
6339 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006340{
6341 /* When the position is not after the current position and in the same
6342 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006343 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006344 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006345 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006346 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006347
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006348 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006349
6350 return (trans ? current_trans_id : current_id);
6351}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006352
Bram Moolenaar860cae12010-06-05 23:22:07 +02006353#if defined(FEAT_CONCEAL) || defined(PROTO)
6354/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006355 * Get extra information about the syntax item. Must be called right after
6356 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006357 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006358 * Returns the current flags.
6359 */
6360 int
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006361get_syntax_info(seqnrp)
6362 int *seqnrp;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006363{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006364 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006365 return current_flags;
6366}
6367
6368/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006369 * Return conceal substitution character
6370 */
6371 int
6372syn_get_sub_char()
6373{
6374 return current_sub_char;
6375}
6376#endif
6377
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006378#if defined(FEAT_EVAL) || defined(PROTO)
6379/*
6380 * Return the syntax ID at position "i" in the current stack.
6381 * The caller must have called syn_get_id() before to fill the stack.
6382 * Returns -1 when "i" is out of range.
6383 */
6384 int
6385syn_get_stack_item(i)
6386 int i;
6387{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006388 if (i >= current_state.ga_len)
6389 {
6390 /* Need to invalidate the state, because we didn't properly finish it
6391 * for the last character, "keep_state" was TRUE. */
6392 invalidate_current_state();
6393 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006394 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006395 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006396 return CUR_STATE(i).si_id;
6397}
6398#endif
6399
Bram Moolenaar071d4272004-06-13 20:20:40 +00006400#if defined(FEAT_FOLDING) || defined(PROTO)
6401/*
6402 * Function called to get folding level for line "lnum" in window "wp".
6403 */
6404 int
6405syn_get_foldlevel(wp, lnum)
6406 win_T *wp;
6407 long lnum;
6408{
6409 int level = 0;
6410 int i;
6411
6412 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006413 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006414 {
6415 syntax_start(wp, lnum);
6416
6417 for (i = 0; i < current_state.ga_len; ++i)
6418 if (CUR_STATE(i).si_flags & HL_FOLD)
6419 ++level;
6420 }
6421 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006422 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006423 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006424 if (level < 0)
6425 level = 0;
6426 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006427 return level;
6428}
6429#endif
6430
6431#endif /* FEAT_SYN_HL */
6432
6433
6434/**************************************
6435 * Highlighting stuff *
6436 **************************************/
6437
6438/*
6439 * The default highlight groups. These are compiled-in for fast startup and
6440 * they still work when the runtime files can't be found.
6441 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006442 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6443 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006444 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006445#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006446# define CENT(a, b) b
6447#else
6448# define CENT(a, b) a
6449#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006450static char *(highlight_init_both[]) =
6451 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006452 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6453 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6454 CENT("IncSearch term=reverse cterm=reverse",
6455 "IncSearch term=reverse cterm=reverse gui=reverse"),
6456 CENT("ModeMsg term=bold cterm=bold",
6457 "ModeMsg term=bold cterm=bold gui=bold"),
6458 CENT("NonText term=bold ctermfg=Blue",
6459 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6460 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6461 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6462 CENT("StatusLineNC term=reverse cterm=reverse",
6463 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006464#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006465 CENT("VertSplit term=reverse cterm=reverse",
6466 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006467#endif
6468#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006469 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6470 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006471#endif
6472#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006473 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6474 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006475#endif
6476#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006477 CENT("PmenuThumb cterm=reverse",
6478 "PmenuThumb cterm=reverse gui=reverse"),
6479 CENT("PmenuSbar ctermbg=Grey",
6480 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006481#endif
6482#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006483 CENT("TabLineSel term=bold cterm=bold",
6484 "TabLineSel term=bold cterm=bold gui=bold"),
6485 CENT("TabLineFill term=reverse cterm=reverse",
6486 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006487#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006488#ifdef FEAT_GUI
6489 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006490 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006491#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006492 NULL
6493 };
6494
6495static char *(highlight_init_light[]) =
6496 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006497 CENT("Directory term=bold ctermfg=DarkBlue",
6498 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6499 CENT("LineNr term=underline ctermfg=Brown",
6500 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6501 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6502 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6503 CENT("Question term=standout ctermfg=DarkGreen",
6504 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6505 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6506 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006507#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006508 CENT("SpellBad term=reverse ctermbg=LightRed",
6509 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6510 CENT("SpellCap term=reverse ctermbg=LightBlue",
6511 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6512 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6513 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6514 CENT("SpellLocal term=underline ctermbg=Cyan",
6515 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006516#endif
6517#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006518 CENT("Pmenu ctermbg=LightMagenta",
6519 "Pmenu ctermbg=LightMagenta guibg=LightMagenta"),
6520 CENT("PmenuSel ctermbg=LightGrey",
6521 "PmenuSel ctermbg=LightGrey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006522#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006523 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6524 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6525 CENT("Title term=bold ctermfg=DarkMagenta",
6526 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6527 CENT("WarningMsg term=standout ctermfg=DarkRed",
6528 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006529#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006530 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6531 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006532#endif
6533#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006534 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6535 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6536 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6537 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006538#endif
6539#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006540 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6541 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006542#endif
6543#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006544 CENT("Visual term=reverse",
6545 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006546#endif
6547#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006548 CENT("DiffAdd term=bold ctermbg=LightBlue",
6549 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6550 CENT("DiffChange term=bold ctermbg=LightMagenta",
6551 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6552 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6553 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006554#endif
6555#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006556 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6557 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006558#endif
6559#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006560 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006561 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006562 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006563 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006564 CENT("ColorColumn term=reverse ctermbg=LightRed",
6565 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006566#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006567#ifdef FEAT_CONCEAL
6568 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6569 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6570#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006571#ifdef FEAT_AUTOCMD
6572 CENT("MatchParen term=reverse ctermbg=Cyan",
6573 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6574#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006575#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006576 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006577#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006578 NULL
6579 };
6580
6581static char *(highlight_init_dark[]) =
6582 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006583 CENT("Directory term=bold ctermfg=LightCyan",
6584 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6585 CENT("LineNr term=underline ctermfg=Yellow",
6586 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6587 CENT("MoreMsg term=bold ctermfg=LightGreen",
6588 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6589 CENT("Question term=standout ctermfg=LightGreen",
6590 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6591 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6592 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6593 CENT("SpecialKey term=bold ctermfg=LightBlue",
6594 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006595#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006596 CENT("SpellBad term=reverse ctermbg=Red",
6597 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6598 CENT("SpellCap term=reverse ctermbg=Blue",
6599 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6600 CENT("SpellRare term=reverse ctermbg=Magenta",
6601 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6602 CENT("SpellLocal term=underline ctermbg=Cyan",
6603 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006604#endif
6605#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006606 CENT("Pmenu ctermbg=Magenta",
6607 "Pmenu ctermbg=Magenta guibg=Magenta"),
6608 CENT("PmenuSel ctermbg=DarkGrey",
6609 "PmenuSel ctermbg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006610#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006611 CENT("Title term=bold ctermfg=LightMagenta",
6612 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6613 CENT("WarningMsg term=standout ctermfg=LightRed",
6614 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006615#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006616 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6617 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006618#endif
6619#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006620 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6621 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6622 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6623 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006624#endif
6625#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006626 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6627 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006628#endif
6629#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006630 CENT("Visual term=reverse",
6631 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006632#endif
6633#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006634 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6635 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6636 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6637 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6638 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6639 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006640#endif
6641#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006642 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6643 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006644#endif
6645#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006646 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006647 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006648 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006649 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006650 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6651 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006652#endif
6653#ifdef FEAT_AUTOCMD
6654 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6655 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006656#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006657#ifdef FEAT_CONCEAL
6658 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6659 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6660#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006661#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006662 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006663#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006664 NULL
6665 };
6666
6667 void
6668init_highlight(both, reset)
6669 int both; /* include groups where 'bg' doesn't matter */
6670 int reset; /* clear group first */
6671{
6672 int i;
6673 char **pp;
6674 static int had_both = FALSE;
6675#ifdef FEAT_EVAL
6676 char_u *p;
6677
6678 /*
6679 * Try finding the color scheme file. Used when a color file was loaded
6680 * and 'background' or 't_Co' is changed.
6681 */
6682 p = get_var_value((char_u *)"g:colors_name");
6683 if (p != NULL && load_colors(p) == OK)
6684 return;
6685#endif
6686
6687 /*
6688 * Didn't use a color file, use the compiled-in colors.
6689 */
6690 if (both)
6691 {
6692 had_both = TRUE;
6693 pp = highlight_init_both;
6694 for (i = 0; pp[i] != NULL; ++i)
6695 do_highlight((char_u *)pp[i], reset, TRUE);
6696 }
6697 else if (!had_both)
6698 /* Don't do anything before the call with both == TRUE from main().
6699 * Not everything has been setup then, and that call will overrule
6700 * everything anyway. */
6701 return;
6702
6703 if (*p_bg == 'l')
6704 pp = highlight_init_light;
6705 else
6706 pp = highlight_init_dark;
6707 for (i = 0; pp[i] != NULL; ++i)
6708 do_highlight((char_u *)pp[i], reset, TRUE);
6709
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006710 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006711 * depend on the number of colors available.
6712 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00006713 * to avoid Statement highlighted text disappears.
6714 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00006715 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00006716 do_highlight((char_u *)(*p_bg == 'l'
6717 ? "Visual cterm=NONE ctermbg=LightGrey"
6718 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006719 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006720 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00006721 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
6722 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006723 if (*p_bg == 'l')
6724 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
6725 }
Bram Moolenaarab194812005-09-14 21:40:12 +00006726
Bram Moolenaar071d4272004-06-13 20:20:40 +00006727#ifdef FEAT_SYN_HL
6728 /*
6729 * If syntax highlighting is enabled load the highlighting for it.
6730 */
6731 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006732 {
6733 static int recursive = 0;
6734
6735 if (recursive >= 5)
6736 EMSG(_("E679: recursive loop loading syncolor.vim"));
6737 else
6738 {
6739 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006740 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006741 --recursive;
6742 }
6743 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006744#endif
6745}
6746
6747/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006748 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006749 * Return OK for success, FAIL for failure.
6750 */
6751 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006752load_colors(name)
6753 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006754{
6755 char_u *buf;
6756 int retval = FAIL;
6757 static int recursive = FALSE;
6758
6759 /* When being called recursively, this is probably because setting
6760 * 'background' caused the highlighting to be reloaded. This means it is
6761 * working, thus we should return OK. */
6762 if (recursive)
6763 return OK;
6764
6765 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006766 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006767 if (buf != NULL)
6768 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006769 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006770 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006771 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006772#ifdef FEAT_AUTOCMD
6773 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6774#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006775 }
6776 recursive = FALSE;
6777
6778 return retval;
6779}
6780
6781/*
6782 * Handle the ":highlight .." command.
6783 * When using ":hi clear" this is called recursively for each group with
6784 * "forceit" and "init" both TRUE.
6785 */
6786 void
6787do_highlight(line, forceit, init)
6788 char_u *line;
6789 int forceit;
6790 int init; /* TRUE when called for initializing */
6791{
6792 char_u *name_end;
6793 char_u *p;
6794 char_u *linep;
6795 char_u *key_start;
6796 char_u *arg_start;
6797 char_u *key = NULL, *arg = NULL;
6798 long i;
6799 int off;
6800 int len;
6801 int attr;
6802 int id;
6803 int idx;
6804 int dodefault = FALSE;
6805 int doclear = FALSE;
6806 int dolink = FALSE;
6807 int error = FALSE;
6808 int color;
6809 int is_normal_group = FALSE; /* "Normal" group */
6810#ifdef FEAT_GUI_X11
6811 int is_menu_group = FALSE; /* "Menu" group */
6812 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6813 int is_tooltip_group = FALSE; /* "Tooltip" group */
6814 int do_colors = FALSE; /* need to update colors? */
6815#else
6816# define is_menu_group 0
6817# define is_tooltip_group 0
6818#endif
6819
6820 /*
6821 * If no argument, list current highlighting.
6822 */
6823 if (ends_excmd(*line))
6824 {
6825 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6826 /* TODO: only call when the group has attributes set */
6827 highlight_list_one((int)i);
6828 return;
6829 }
6830
6831 /*
6832 * Isolate the name.
6833 */
6834 name_end = skiptowhite(line);
6835 linep = skipwhite(name_end);
6836
6837 /*
6838 * Check for "default" argument.
6839 */
6840 if (STRNCMP(line, "default", name_end - line) == 0)
6841 {
6842 dodefault = TRUE;
6843 line = linep;
6844 name_end = skiptowhite(line);
6845 linep = skipwhite(name_end);
6846 }
6847
6848 /*
6849 * Check for "clear" or "link" argument.
6850 */
6851 if (STRNCMP(line, "clear", name_end - line) == 0)
6852 doclear = TRUE;
6853 if (STRNCMP(line, "link", name_end - line) == 0)
6854 dolink = TRUE;
6855
6856 /*
6857 * ":highlight {group-name}": list highlighting for one group.
6858 */
6859 if (!doclear && !dolink && ends_excmd(*linep))
6860 {
6861 id = syn_namen2id(line, (int)(name_end - line));
6862 if (id == 0)
6863 EMSG2(_("E411: highlight group not found: %s"), line);
6864 else
6865 highlight_list_one(id);
6866 return;
6867 }
6868
6869 /*
6870 * Handle ":highlight link {from} {to}" command.
6871 */
6872 if (dolink)
6873 {
6874 char_u *from_start = linep;
6875 char_u *from_end;
6876 char_u *to_start;
6877 char_u *to_end;
6878 int from_id;
6879 int to_id;
6880
6881 from_end = skiptowhite(from_start);
6882 to_start = skipwhite(from_end);
6883 to_end = skiptowhite(to_start);
6884
6885 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6886 {
6887 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6888 from_start);
6889 return;
6890 }
6891
6892 if (!ends_excmd(*skipwhite(to_end)))
6893 {
6894 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6895 return;
6896 }
6897
6898 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6899 if (STRNCMP(to_start, "NONE", 4) == 0)
6900 to_id = 0;
6901 else
6902 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6903
6904 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6905 {
6906 /*
6907 * Don't allow a link when there already is some highlighting
6908 * for the group, unless '!' is used
6909 */
6910 if (to_id > 0 && !forceit && !init
6911 && hl_has_settings(from_id - 1, dodefault))
6912 {
6913 if (sourcing_name == NULL && !dodefault)
6914 EMSG(_("E414: group has settings, highlight link ignored"));
6915 }
6916 else
6917 {
6918 if (!init)
6919 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6920 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00006921#ifdef FEAT_EVAL
6922 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6923#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00006924 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006925 }
6926 }
6927
6928 /* Only call highlight_changed() once, after sourcing a syntax file */
6929 need_highlight_changed = TRUE;
6930
6931 return;
6932 }
6933
6934 if (doclear)
6935 {
6936 /*
6937 * ":highlight clear [group]" command.
6938 */
6939 line = linep;
6940 if (ends_excmd(*line))
6941 {
6942#ifdef FEAT_GUI
6943 /* First, we do not destroy the old values, but allocate the new
6944 * ones and update the display. THEN we destroy the old values.
6945 * If we destroy the old values first, then the old values
6946 * (such as GuiFont's or GuiFontset's) will still be displayed but
6947 * invalid because they were free'd.
6948 */
6949 if (gui.in_use)
6950 {
6951# ifdef FEAT_BEVAL_TIP
6952 gui_init_tooltip_font();
6953# endif
6954# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6955 gui_init_menu_font();
6956# endif
6957 }
6958# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6959 gui_mch_def_colors();
6960# endif
6961# ifdef FEAT_GUI_X11
6962# ifdef FEAT_MENU
6963
6964 /* This only needs to be done when there is no Menu highlight
6965 * group defined by default, which IS currently the case.
6966 */
6967 gui_mch_new_menu_colors();
6968# endif
6969 if (gui.in_use)
6970 {
6971 gui_new_scrollbar_colors();
6972# ifdef FEAT_BEVAL
6973 gui_mch_new_tooltip_colors();
6974# endif
6975# ifdef FEAT_MENU
6976 gui_mch_new_menu_font();
6977# endif
6978 }
6979# endif
6980
6981 /* Ok, we're done allocating the new default graphics items.
6982 * The screen should already be refreshed at this point.
6983 * It is now Ok to clear out the old data.
6984 */
6985#endif
6986#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00006987 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006988#endif
6989 restore_cterm_colors();
6990
6991 /*
6992 * Clear all default highlight groups and load the defaults.
6993 */
6994 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6995 highlight_clear(idx);
6996 init_highlight(TRUE, TRUE);
6997#ifdef FEAT_GUI
6998 if (gui.in_use)
6999 highlight_gui_started();
7000#endif
7001 highlight_changed();
7002 redraw_later_clear();
7003 return;
7004 }
7005 name_end = skiptowhite(line);
7006 linep = skipwhite(name_end);
7007 }
7008
7009 /*
7010 * Find the group name in the table. If it does not exist yet, add it.
7011 */
7012 id = syn_check_group(line, (int)(name_end - line));
7013 if (id == 0) /* failed (out of memory) */
7014 return;
7015 idx = id - 1; /* index is ID minus one */
7016
7017 /* Return if "default" was used and the group already has settings. */
7018 if (dodefault && hl_has_settings(idx, TRUE))
7019 return;
7020
7021 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7022 is_normal_group = TRUE;
7023#ifdef FEAT_GUI_X11
7024 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7025 is_menu_group = TRUE;
7026 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7027 is_scrollbar_group = TRUE;
7028 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7029 is_tooltip_group = TRUE;
7030#endif
7031
7032 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7033 if (doclear || (forceit && init))
7034 {
7035 highlight_clear(idx);
7036 if (!doclear)
7037 HL_TABLE()[idx].sg_set = 0;
7038 }
7039
7040 if (!doclear)
7041 while (!ends_excmd(*linep))
7042 {
7043 key_start = linep;
7044 if (*linep == '=')
7045 {
7046 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7047 error = TRUE;
7048 break;
7049 }
7050
7051 /*
7052 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7053 * "guibg").
7054 */
7055 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7056 ++linep;
7057 vim_free(key);
7058 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7059 if (key == NULL)
7060 {
7061 error = TRUE;
7062 break;
7063 }
7064 linep = skipwhite(linep);
7065
7066 if (STRCMP(key, "NONE") == 0)
7067 {
7068 if (!init || HL_TABLE()[idx].sg_set == 0)
7069 {
7070 if (!init)
7071 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7072 highlight_clear(idx);
7073 }
7074 continue;
7075 }
7076
7077 /*
7078 * Check for the equal sign.
7079 */
7080 if (*linep != '=')
7081 {
7082 EMSG2(_("E416: missing equal sign: %s"), key_start);
7083 error = TRUE;
7084 break;
7085 }
7086 ++linep;
7087
7088 /*
7089 * Isolate the argument.
7090 */
7091 linep = skipwhite(linep);
7092 if (*linep == '\'') /* guifg='color name' */
7093 {
7094 arg_start = ++linep;
7095 linep = vim_strchr(linep, '\'');
7096 if (linep == NULL)
7097 {
7098 EMSG2(_(e_invarg2), key_start);
7099 error = TRUE;
7100 break;
7101 }
7102 }
7103 else
7104 {
7105 arg_start = linep;
7106 linep = skiptowhite(linep);
7107 }
7108 if (linep == arg_start)
7109 {
7110 EMSG2(_("E417: missing argument: %s"), key_start);
7111 error = TRUE;
7112 break;
7113 }
7114 vim_free(arg);
7115 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7116 if (arg == NULL)
7117 {
7118 error = TRUE;
7119 break;
7120 }
7121 if (*linep == '\'')
7122 ++linep;
7123
7124 /*
7125 * Store the argument.
7126 */
7127 if ( STRCMP(key, "TERM") == 0
7128 || STRCMP(key, "CTERM") == 0
7129 || STRCMP(key, "GUI") == 0)
7130 {
7131 attr = 0;
7132 off = 0;
7133 while (arg[off] != NUL)
7134 {
7135 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7136 {
7137 len = (int)STRLEN(hl_name_table[i]);
7138 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7139 {
7140 attr |= hl_attr_table[i];
7141 off += len;
7142 break;
7143 }
7144 }
7145 if (i < 0)
7146 {
7147 EMSG2(_("E418: Illegal value: %s"), arg);
7148 error = TRUE;
7149 break;
7150 }
7151 if (arg[off] == ',') /* another one follows */
7152 ++off;
7153 }
7154 if (error)
7155 break;
7156 if (*key == 'T')
7157 {
7158 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7159 {
7160 if (!init)
7161 HL_TABLE()[idx].sg_set |= SG_TERM;
7162 HL_TABLE()[idx].sg_term = attr;
7163 }
7164 }
7165 else if (*key == 'C')
7166 {
7167 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7168 {
7169 if (!init)
7170 HL_TABLE()[idx].sg_set |= SG_CTERM;
7171 HL_TABLE()[idx].sg_cterm = attr;
7172 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7173 }
7174 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007175#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007176 else
7177 {
7178 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7179 {
7180 if (!init)
7181 HL_TABLE()[idx].sg_set |= SG_GUI;
7182 HL_TABLE()[idx].sg_gui = attr;
7183 }
7184 }
7185#endif
7186 }
7187 else if (STRCMP(key, "FONT") == 0)
7188 {
7189 /* in non-GUI fonts are simply ignored */
7190#ifdef FEAT_GUI
7191 if (!gui.shell_created)
7192 {
7193 /* GUI not started yet, always accept the name. */
7194 vim_free(HL_TABLE()[idx].sg_font_name);
7195 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7196 }
7197 else
7198 {
7199 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7200# ifdef FEAT_XFONTSET
7201 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7202# endif
7203 /* First, save the current font/fontset.
7204 * Then try to allocate the font/fontset.
7205 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7206 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7207 */
7208
7209 HL_TABLE()[idx].sg_font = NOFONT;
7210# ifdef FEAT_XFONTSET
7211 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7212# endif
7213 hl_do_font(idx, arg, is_normal_group, is_menu_group,
7214 is_tooltip_group);
7215
7216# ifdef FEAT_XFONTSET
7217 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7218 {
7219 /* New fontset was accepted. Free the old one, if there was
7220 * one.
7221 */
7222 gui_mch_free_fontset(temp_sg_fontset);
7223 vim_free(HL_TABLE()[idx].sg_font_name);
7224 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7225 }
7226 else
7227 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7228# endif
7229 if (HL_TABLE()[idx].sg_font != NOFONT)
7230 {
7231 /* New font was accepted. Free the old one, if there was
7232 * one.
7233 */
7234 gui_mch_free_font(temp_sg_font);
7235 vim_free(HL_TABLE()[idx].sg_font_name);
7236 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7237 }
7238 else
7239 HL_TABLE()[idx].sg_font = temp_sg_font;
7240 }
7241#endif
7242 }
7243 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7244 {
7245 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7246 {
7247 if (!init)
7248 HL_TABLE()[idx].sg_set |= SG_CTERM;
7249
7250 /* When setting the foreground color, and previously the "bold"
7251 * flag was set for a light color, reset it now */
7252 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7253 {
7254 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7255 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7256 }
7257
7258 if (VIM_ISDIGIT(*arg))
7259 color = atoi((char *)arg);
7260 else if (STRICMP(arg, "fg") == 0)
7261 {
7262 if (cterm_normal_fg_color)
7263 color = cterm_normal_fg_color - 1;
7264 else
7265 {
7266 EMSG(_("E419: FG color unknown"));
7267 error = TRUE;
7268 break;
7269 }
7270 }
7271 else if (STRICMP(arg, "bg") == 0)
7272 {
7273 if (cterm_normal_bg_color > 0)
7274 color = cterm_normal_bg_color - 1;
7275 else
7276 {
7277 EMSG(_("E420: BG color unknown"));
7278 error = TRUE;
7279 break;
7280 }
7281 }
7282 else
7283 {
7284 static char *(color_names[28]) = {
7285 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7286 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7287 "Gray", "Grey",
7288 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7289 "Blue", "LightBlue", "Green", "LightGreen",
7290 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7291 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7292 static int color_numbers_16[28] = {0, 1, 2, 3,
7293 4, 5, 6, 6,
7294 7, 7,
7295 7, 7, 8, 8,
7296 9, 9, 10, 10,
7297 11, 11, 12, 12, 13,
7298 13, 14, 14, 15, -1};
7299 /* for xterm with 88 colors... */
7300 static int color_numbers_88[28] = {0, 4, 2, 6,
7301 1, 5, 32, 72,
7302 84, 84,
7303 7, 7, 82, 82,
7304 12, 43, 10, 61,
7305 14, 63, 9, 74, 13,
7306 75, 11, 78, 15, -1};
7307 /* for xterm with 256 colors... */
7308 static int color_numbers_256[28] = {0, 4, 2, 6,
7309 1, 5, 130, 130,
7310 248, 248,
7311 7, 7, 242, 242,
7312 12, 81, 10, 121,
7313 14, 159, 9, 224, 13,
7314 225, 11, 229, 15, -1};
7315 /* for terminals with less than 16 colors... */
7316 static int color_numbers_8[28] = {0, 4, 2, 6,
7317 1, 5, 3, 3,
7318 7, 7,
7319 7, 7, 0+8, 0+8,
7320 4+8, 4+8, 2+8, 2+8,
7321 6+8, 6+8, 1+8, 1+8, 5+8,
7322 5+8, 3+8, 3+8, 7+8, -1};
7323#if defined(__QNXNTO__)
7324 static int *color_numbers_8_qansi = color_numbers_8;
7325 /* On qnx, the 8 & 16 color arrays are the same */
7326 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7327 color_numbers_8_qansi = color_numbers_16;
7328#endif
7329
7330 /* reduce calls to STRICMP a bit, it can be slow */
7331 off = TOUPPER_ASC(*arg);
7332 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7333 if (off == color_names[i][0]
7334 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7335 break;
7336 if (i < 0)
7337 {
7338 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7339 error = TRUE;
7340 break;
7341 }
7342
7343 /* Use the _16 table to check if its a valid color name. */
7344 color = color_numbers_16[i];
7345 if (color >= 0)
7346 {
7347 if (t_colors == 8)
7348 {
7349 /* t_Co is 8: use the 8 colors table */
7350#if defined(__QNXNTO__)
7351 color = color_numbers_8_qansi[i];
7352#else
7353 color = color_numbers_8[i];
7354#endif
7355 if (key[5] == 'F')
7356 {
7357 /* set/reset bold attribute to get light foreground
7358 * colors (on some terminals, e.g. "linux") */
7359 if (color & 8)
7360 {
7361 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7362 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7363 }
7364 else
7365 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7366 }
7367 color &= 7; /* truncate to 8 colors */
7368 }
7369 else if (t_colors == 16 || t_colors == 88
7370 || t_colors == 256)
7371 {
7372 /*
7373 * Guess: if the termcap entry ends in 'm', it is
7374 * probably an xterm-like terminal. Use the changed
7375 * order for colors.
7376 */
7377 if (*T_CAF != NUL)
7378 p = T_CAF;
7379 else
7380 p = T_CSF;
7381 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7382 switch (t_colors)
7383 {
7384 case 16:
7385 color = color_numbers_8[i];
7386 break;
7387 case 88:
7388 color = color_numbers_88[i];
7389 break;
7390 case 256:
7391 color = color_numbers_256[i];
7392 break;
7393 }
7394 }
7395 }
7396 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007397 /* Add one to the argument, to avoid zero. Zero is used for
7398 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007399 if (key[5] == 'F')
7400 {
7401 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7402 if (is_normal_group)
7403 {
7404 cterm_normal_fg_color = color + 1;
7405 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7406#ifdef FEAT_GUI
7407 /* Don't do this if the GUI is used. */
7408 if (!gui.in_use && !gui.starting)
7409#endif
7410 {
7411 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007412 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007413 term_fg_color(color);
7414 }
7415 }
7416 }
7417 else
7418 {
7419 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7420 if (is_normal_group)
7421 {
7422 cterm_normal_bg_color = color + 1;
7423#ifdef FEAT_GUI
7424 /* Don't mess with 'background' if the GUI is used. */
7425 if (!gui.in_use && !gui.starting)
7426#endif
7427 {
7428 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007429 if (color >= 0)
7430 {
7431 if (termcap_active)
7432 term_bg_color(color);
7433 if (t_colors < 16)
7434 i = (color == 0 || color == 4);
7435 else
7436 i = (color < 7 || color == 8);
7437 /* Set the 'background' option if the value is
7438 * wrong. */
7439 if (i != (*p_bg == 'd'))
7440 set_option_value((char_u *)"bg", 0L,
7441 i ? (char_u *)"dark"
7442 : (char_u *)"light", 0);
7443 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007444 }
7445 }
7446 }
7447 }
7448 }
7449 else if (STRCMP(key, "GUIFG") == 0)
7450 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007451#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007452 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007453 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007454 if (!init)
7455 HL_TABLE()[idx].sg_set |= SG_GUI;
7456
Bram Moolenaar61623362010-07-14 22:04:22 +02007457# ifdef FEAT_GUI
7458 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007459 i = color_name2handle(arg);
7460 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7461 {
7462 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007463# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007464 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7465 if (STRCMP(arg, "NONE"))
7466 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7467 else
7468 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007469# ifdef FEAT_GUI
7470# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007471 if (is_menu_group)
7472 gui.menu_fg_pixel = i;
7473 if (is_scrollbar_group)
7474 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007475# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007476 if (is_tooltip_group)
7477 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007478# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007479 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007480# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007481 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007482# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007483 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007484#endif
7485 }
7486 else if (STRCMP(key, "GUIBG") == 0)
7487 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007488#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007489 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007490 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007491 if (!init)
7492 HL_TABLE()[idx].sg_set |= SG_GUI;
7493
Bram Moolenaar61623362010-07-14 22:04:22 +02007494# ifdef FEAT_GUI
7495 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007496 i = color_name2handle(arg);
7497 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7498 {
7499 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007500# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007501 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7502 if (STRCMP(arg, "NONE") != 0)
7503 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7504 else
7505 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007506# ifdef FEAT_GUI
7507# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007508 if (is_menu_group)
7509 gui.menu_bg_pixel = i;
7510 if (is_scrollbar_group)
7511 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007512# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007513 if (is_tooltip_group)
7514 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007515# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007516 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007517# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007518 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007519# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007520 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007521#endif
7522 }
7523 else if (STRCMP(key, "GUISP") == 0)
7524 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007525#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007526 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7527 {
7528 if (!init)
7529 HL_TABLE()[idx].sg_set |= SG_GUI;
7530
Bram Moolenaar61623362010-07-14 22:04:22 +02007531# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007532 i = color_name2handle(arg);
7533 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7534 {
7535 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007536# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007537 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7538 if (STRCMP(arg, "NONE") != 0)
7539 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7540 else
7541 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007542# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007543 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007544# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007545 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007546#endif
7547 }
7548 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7549 {
7550 char_u buf[100];
7551 char_u *tname;
7552
7553 if (!init)
7554 HL_TABLE()[idx].sg_set |= SG_TERM;
7555
7556 /*
7557 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007558 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007559 */
7560 if (STRNCMP(arg, "t_", 2) == 0)
7561 {
7562 off = 0;
7563 buf[0] = 0;
7564 while (arg[off] != NUL)
7565 {
7566 /* Isolate one termcap name */
7567 for (len = 0; arg[off + len] &&
7568 arg[off + len] != ','; ++len)
7569 ;
7570 tname = vim_strnsave(arg + off, len);
7571 if (tname == NULL) /* out of memory */
7572 {
7573 error = TRUE;
7574 break;
7575 }
7576 /* lookup the escape sequence for the item */
7577 p = get_term_code(tname);
7578 vim_free(tname);
7579 if (p == NULL) /* ignore non-existing things */
7580 p = (char_u *)"";
7581
7582 /* Append it to the already found stuff */
7583 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7584 {
7585 EMSG2(_("E422: terminal code too long: %s"), arg);
7586 error = TRUE;
7587 break;
7588 }
7589 STRCAT(buf, p);
7590
7591 /* Advance to the next item */
7592 off += len;
7593 if (arg[off] == ',') /* another one follows */
7594 ++off;
7595 }
7596 }
7597 else
7598 {
7599 /*
7600 * Copy characters from arg[] to buf[], translating <> codes.
7601 */
7602 for (p = arg, off = 0; off < 100 && *p; )
7603 {
7604 len = trans_special(&p, buf + off, FALSE);
7605 if (len) /* recognized special char */
7606 off += len;
7607 else /* copy as normal char */
7608 buf[off++] = *p++;
7609 }
7610 buf[off] = NUL;
7611 }
7612 if (error)
7613 break;
7614
7615 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7616 p = NULL;
7617 else
7618 p = vim_strsave(buf);
7619 if (key[2] == 'A')
7620 {
7621 vim_free(HL_TABLE()[idx].sg_start);
7622 HL_TABLE()[idx].sg_start = p;
7623 }
7624 else
7625 {
7626 vim_free(HL_TABLE()[idx].sg_stop);
7627 HL_TABLE()[idx].sg_stop = p;
7628 }
7629 }
7630 else
7631 {
7632 EMSG2(_("E423: Illegal argument: %s"), key_start);
7633 error = TRUE;
7634 break;
7635 }
7636
7637 /*
7638 * When highlighting has been given for a group, don't link it.
7639 */
7640 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7641 HL_TABLE()[idx].sg_link = 0;
7642
7643 /*
7644 * Continue with next argument.
7645 */
7646 linep = skipwhite(linep);
7647 }
7648
7649 /*
7650 * If there is an error, and it's a new entry, remove it from the table.
7651 */
7652 if (error && idx == highlight_ga.ga_len)
7653 syn_unadd_group();
7654 else
7655 {
7656 if (is_normal_group)
7657 {
7658 HL_TABLE()[idx].sg_term_attr = 0;
7659 HL_TABLE()[idx].sg_cterm_attr = 0;
7660#ifdef FEAT_GUI
7661 HL_TABLE()[idx].sg_gui_attr = 0;
7662 /*
7663 * Need to update all groups, because they might be using "bg"
7664 * and/or "fg", which have been changed now.
7665 */
7666 if (gui.in_use)
7667 highlight_gui_started();
7668#endif
7669 }
7670#ifdef FEAT_GUI_X11
7671# ifdef FEAT_MENU
7672 else if (is_menu_group)
7673 {
7674 if (gui.in_use && do_colors)
7675 gui_mch_new_menu_colors();
7676 }
7677# endif
7678 else if (is_scrollbar_group)
7679 {
7680 if (gui.in_use && do_colors)
7681 gui_new_scrollbar_colors();
7682 }
7683# ifdef FEAT_BEVAL
7684 else if (is_tooltip_group)
7685 {
7686 if (gui.in_use && do_colors)
7687 gui_mch_new_tooltip_colors();
7688 }
7689# endif
7690#endif
7691 else
7692 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007693#ifdef FEAT_EVAL
7694 HL_TABLE()[idx].sg_scriptID = current_SID;
7695#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007696 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007697 }
7698 vim_free(key);
7699 vim_free(arg);
7700
7701 /* Only call highlight_changed() once, after sourcing a syntax file */
7702 need_highlight_changed = TRUE;
7703}
7704
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007705#if defined(EXITFREE) || defined(PROTO)
7706 void
7707free_highlight()
7708{
7709 int i;
7710
7711 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007712 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007713 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007714 vim_free(HL_TABLE()[i].sg_name);
7715 vim_free(HL_TABLE()[i].sg_name_u);
7716 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007717 ga_clear(&highlight_ga);
7718}
7719#endif
7720
Bram Moolenaar071d4272004-06-13 20:20:40 +00007721/*
7722 * Reset the cterm colors to what they were before Vim was started, if
7723 * possible. Otherwise reset them to zero.
7724 */
7725 void
7726restore_cterm_colors()
7727{
7728#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7729 /* Since t_me has been set, this probably means that the user
7730 * wants to use this as default colors. Need to reset default
7731 * background/foreground colors. */
7732 mch_set_normal_colors();
7733#else
7734 cterm_normal_fg_color = 0;
7735 cterm_normal_fg_bold = 0;
7736 cterm_normal_bg_color = 0;
7737#endif
7738}
7739
7740/*
7741 * Return TRUE if highlight group "idx" has any settings.
7742 * When "check_link" is TRUE also check for an existing link.
7743 */
7744 static int
7745hl_has_settings(idx, check_link)
7746 int idx;
7747 int check_link;
7748{
7749 return ( HL_TABLE()[idx].sg_term_attr != 0
7750 || HL_TABLE()[idx].sg_cterm_attr != 0
7751#ifdef FEAT_GUI
7752 || HL_TABLE()[idx].sg_gui_attr != 0
7753#endif
7754 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7755}
7756
7757/*
7758 * Clear highlighting for one group.
7759 */
7760 static void
7761highlight_clear(idx)
7762 int idx;
7763{
7764 HL_TABLE()[idx].sg_term = 0;
7765 vim_free(HL_TABLE()[idx].sg_start);
7766 HL_TABLE()[idx].sg_start = NULL;
7767 vim_free(HL_TABLE()[idx].sg_stop);
7768 HL_TABLE()[idx].sg_stop = NULL;
7769 HL_TABLE()[idx].sg_term_attr = 0;
7770 HL_TABLE()[idx].sg_cterm = 0;
7771 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7772 HL_TABLE()[idx].sg_cterm_fg = 0;
7773 HL_TABLE()[idx].sg_cterm_bg = 0;
7774 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02007775#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007776 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007777 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7778 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007779 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7780 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007781 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7782 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007783#endif
7784#ifdef FEAT_GUI
7785 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7786 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7787 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007788 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7789 HL_TABLE()[idx].sg_font = NOFONT;
7790# ifdef FEAT_XFONTSET
7791 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7792 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7793# endif
7794 vim_free(HL_TABLE()[idx].sg_font_name);
7795 HL_TABLE()[idx].sg_font_name = NULL;
7796 HL_TABLE()[idx].sg_gui_attr = 0;
7797#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00007798#ifdef FEAT_EVAL
7799 /* Clear the script ID only when there is no link, since that is not
7800 * cleared. */
7801 if (HL_TABLE()[idx].sg_link == 0)
7802 HL_TABLE()[idx].sg_scriptID = 0;
7803#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007804}
7805
7806#if defined(FEAT_GUI) || defined(PROTO)
7807/*
7808 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00007809 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00007810 * "Tooltip" colors.
7811 */
7812 void
7813set_normal_colors()
7814{
7815 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007816 &gui.norm_pixel, &gui.back_pixel,
7817 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007818 {
7819 gui_mch_new_colors();
7820 must_redraw = CLEAR;
7821 }
7822#ifdef FEAT_GUI_X11
7823 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007824 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7825 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007826 {
7827# ifdef FEAT_MENU
7828 gui_mch_new_menu_colors();
7829# endif
7830 must_redraw = CLEAR;
7831 }
7832# ifdef FEAT_BEVAL
7833 if (set_group_colors((char_u *)"Tooltip",
7834 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7835 FALSE, FALSE, TRUE))
7836 {
7837# ifdef FEAT_TOOLBAR
7838 gui_mch_new_tooltip_colors();
7839# endif
7840 must_redraw = CLEAR;
7841 }
7842#endif
7843 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007844 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7845 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007846 {
7847 gui_new_scrollbar_colors();
7848 must_redraw = CLEAR;
7849 }
7850#endif
7851}
7852
7853/*
7854 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7855 */
7856 static int
7857set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7858 char_u *name;
7859 guicolor_T *fgp;
7860 guicolor_T *bgp;
7861 int do_menu;
7862 int use_norm;
7863 int do_tooltip;
7864{
7865 int idx;
7866
7867 idx = syn_name2id(name) - 1;
7868 if (idx >= 0)
7869 {
7870 gui_do_one_color(idx, do_menu, do_tooltip);
7871
7872 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7873 *fgp = HL_TABLE()[idx].sg_gui_fg;
7874 else if (use_norm)
7875 *fgp = gui.def_norm_pixel;
7876 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7877 *bgp = HL_TABLE()[idx].sg_gui_bg;
7878 else if (use_norm)
7879 *bgp = gui.def_back_pixel;
7880 return TRUE;
7881 }
7882 return FALSE;
7883}
7884
7885/*
7886 * Get the font of the "Normal" group.
7887 * Returns "" when it's not found or not set.
7888 */
7889 char_u *
7890hl_get_font_name()
7891{
7892 int id;
7893 char_u *s;
7894
7895 id = syn_name2id((char_u *)"Normal");
7896 if (id > 0)
7897 {
7898 s = HL_TABLE()[id - 1].sg_font_name;
7899 if (s != NULL)
7900 return s;
7901 }
7902 return (char_u *)"";
7903}
7904
7905/*
7906 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7907 * actually chosen to be used.
7908 */
7909 void
7910hl_set_font_name(font_name)
7911 char_u *font_name;
7912{
7913 int id;
7914
7915 id = syn_name2id((char_u *)"Normal");
7916 if (id > 0)
7917 {
7918 vim_free(HL_TABLE()[id - 1].sg_font_name);
7919 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7920 }
7921}
7922
7923/*
7924 * Set background color for "Normal" group. Called by gui_set_bg_color()
7925 * when the color is known.
7926 */
7927 void
7928hl_set_bg_color_name(name)
7929 char_u *name; /* must have been allocated */
7930{
7931 int id;
7932
7933 if (name != NULL)
7934 {
7935 id = syn_name2id((char_u *)"Normal");
7936 if (id > 0)
7937 {
7938 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7939 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7940 }
7941 }
7942}
7943
7944/*
7945 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7946 * when the color is known.
7947 */
7948 void
7949hl_set_fg_color_name(name)
7950 char_u *name; /* must have been allocated */
7951{
7952 int id;
7953
7954 if (name != NULL)
7955 {
7956 id = syn_name2id((char_u *)"Normal");
7957 if (id > 0)
7958 {
7959 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7960 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7961 }
7962 }
7963}
7964
7965/*
7966 * Return the handle for a color name.
7967 * Returns INVALCOLOR when failed.
7968 */
7969 static guicolor_T
7970color_name2handle(name)
7971 char_u *name;
7972{
7973 if (STRCMP(name, "NONE") == 0)
7974 return INVALCOLOR;
7975
7976 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7977 return gui.norm_pixel;
7978 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7979 return gui.back_pixel;
7980
7981 return gui_get_color(name);
7982}
7983
7984/*
7985 * Return the handle for a font name.
7986 * Returns NOFONT when failed.
7987 */
7988 static GuiFont
7989font_name2handle(name)
7990 char_u *name;
7991{
7992 if (STRCMP(name, "NONE") == 0)
7993 return NOFONT;
7994
7995 return gui_mch_get_font(name, TRUE);
7996}
7997
7998# ifdef FEAT_XFONTSET
7999/*
8000 * Return the handle for a fontset name.
8001 * Returns NOFONTSET when failed.
8002 */
8003 static GuiFontset
8004fontset_name2handle(name, fixed_width)
8005 char_u *name;
8006 int fixed_width;
8007{
8008 if (STRCMP(name, "NONE") == 0)
8009 return NOFONTSET;
8010
8011 return gui_mch_get_fontset(name, TRUE, fixed_width);
8012}
8013# endif
8014
8015/*
8016 * Get the font or fontset for one highlight group.
8017 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008018 static void
8019hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
8020 int idx;
8021 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00008022 int do_normal; /* set normal font */
8023 int do_menu UNUSED; /* set menu font */
8024 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008025{
8026# ifdef FEAT_XFONTSET
8027 /* If 'guifontset' is not empty, first try using the name as a
8028 * fontset. If that doesn't work, use it as a font name. */
8029 if (*p_guifontset != NUL
8030# ifdef FONTSET_ALWAYS
8031 || do_menu
8032# endif
8033# ifdef FEAT_BEVAL_TIP
8034 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8035 || do_tooltip
8036# endif
8037 )
8038 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8039# ifdef FONTSET_ALWAYS
8040 || do_menu
8041# endif
8042# ifdef FEAT_BEVAL_TIP
8043 || do_tooltip
8044# endif
8045 );
8046 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8047 {
8048 /* If it worked and it's the Normal group, use it as the
8049 * normal fontset. Same for the Menu group. */
8050 if (do_normal)
8051 gui_init_font(arg, TRUE);
8052# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8053 if (do_menu)
8054 {
8055# ifdef FONTSET_ALWAYS
8056 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8057# else
8058 /* YIKES! This is a bug waiting to crash the program */
8059 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8060# endif
8061 gui_mch_new_menu_font();
8062 }
8063# ifdef FEAT_BEVAL
8064 if (do_tooltip)
8065 {
8066 /* The Athena widget set cannot currently handle switching between
8067 * displaying a single font and a fontset.
8068 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008069 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008070 * XFontStruct is used.
8071 */
8072 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8073 gui_mch_new_tooltip_font();
8074 }
8075# endif
8076# endif
8077 }
8078 else
8079# endif
8080 {
8081 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8082 /* If it worked and it's the Normal group, use it as the
8083 * normal font. Same for the Menu group. */
8084 if (HL_TABLE()[idx].sg_font != NOFONT)
8085 {
8086 if (do_normal)
8087 gui_init_font(arg, FALSE);
8088#ifndef FONTSET_ALWAYS
8089# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8090 if (do_menu)
8091 {
8092 gui.menu_font = HL_TABLE()[idx].sg_font;
8093 gui_mch_new_menu_font();
8094 }
8095# endif
8096#endif
8097 }
8098 }
8099}
8100
8101#endif /* FEAT_GUI */
8102
8103/*
8104 * Table with the specifications for an attribute number.
8105 * Note that this table is used by ALL buffers. This is required because the
8106 * GUI can redraw at any time for any buffer.
8107 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008108static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008109
8110#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8111
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008112static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008113
8114#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8115
8116#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008117static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008118
8119#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8120#endif
8121
8122/*
8123 * Return the attr number for a set of colors and font.
8124 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8125 * if the combination is new.
8126 * Return 0 for error (no more room).
8127 */
8128 static int
8129get_attr_entry(table, aep)
8130 garray_T *table;
8131 attrentry_T *aep;
8132{
8133 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008134 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008135 static int recursive = FALSE;
8136
8137 /*
8138 * Init the table, in case it wasn't done yet.
8139 */
8140 table->ga_itemsize = sizeof(attrentry_T);
8141 table->ga_growsize = 7;
8142
8143 /*
8144 * Try to find an entry with the same specifications.
8145 */
8146 for (i = 0; i < table->ga_len; ++i)
8147 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008148 taep = &(((attrentry_T *)table->ga_data)[i]);
8149 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008150 && (
8151#ifdef FEAT_GUI
8152 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008153 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8154 && aep->ae_u.gui.bg_color
8155 == taep->ae_u.gui.bg_color
8156 && aep->ae_u.gui.sp_color
8157 == taep->ae_u.gui.sp_color
8158 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008159# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008160 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008161# endif
8162 ))
8163 ||
8164#endif
8165 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008166 && (aep->ae_u.term.start == NULL)
8167 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008168 && (aep->ae_u.term.start == NULL
8169 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008170 taep->ae_u.term.start) == 0)
8171 && (aep->ae_u.term.stop == NULL)
8172 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008173 && (aep->ae_u.term.stop == NULL
8174 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008175 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008176 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008177 && aep->ae_u.cterm.fg_color
8178 == taep->ae_u.cterm.fg_color
8179 && aep->ae_u.cterm.bg_color
8180 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008181 ))
8182
8183 return i + ATTR_OFF;
8184 }
8185
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008186 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008187 {
8188 /*
8189 * Running out of attribute entries! remove all attributes, and
8190 * compute new ones for all groups.
8191 * When called recursively, we are really out of numbers.
8192 */
8193 if (recursive)
8194 {
8195 EMSG(_("E424: Too many different highlighting attributes in use"));
8196 return 0;
8197 }
8198 recursive = TRUE;
8199
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008200 clear_hl_tables();
8201
Bram Moolenaar071d4272004-06-13 20:20:40 +00008202 must_redraw = CLEAR;
8203
8204 for (i = 0; i < highlight_ga.ga_len; ++i)
8205 set_hl_attr(i);
8206
8207 recursive = FALSE;
8208 }
8209
8210 /*
8211 * This is a new combination of colors and font, add an entry.
8212 */
8213 if (ga_grow(table, 1) == FAIL)
8214 return 0;
8215
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008216 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8217 vim_memset(taep, 0, sizeof(attrentry_T));
8218 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008219#ifdef FEAT_GUI
8220 if (table == &gui_attr_table)
8221 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008222 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8223 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8224 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8225 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008226# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008227 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008228# endif
8229 }
8230#endif
8231 if (table == &term_attr_table)
8232 {
8233 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008234 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008235 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008236 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008237 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008238 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008239 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008240 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008241 }
8242 else if (table == &cterm_attr_table)
8243 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008244 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8245 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008246 }
8247 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008248 return (table->ga_len - 1 + ATTR_OFF);
8249}
8250
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008251/*
8252 * Clear all highlight tables.
8253 */
8254 void
8255clear_hl_tables()
8256{
8257 int i;
8258 attrentry_T *taep;
8259
8260#ifdef FEAT_GUI
8261 ga_clear(&gui_attr_table);
8262#endif
8263 for (i = 0; i < term_attr_table.ga_len; ++i)
8264 {
8265 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8266 vim_free(taep->ae_u.term.start);
8267 vim_free(taep->ae_u.term.stop);
8268 }
8269 ga_clear(&term_attr_table);
8270 ga_clear(&cterm_attr_table);
8271}
8272
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008273#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008274/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008275 * Combine special attributes (e.g., for spelling) with other attributes
8276 * (e.g., for syntax highlighting).
8277 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008278 * This creates a new group when required.
8279 * Since we expect there to be few spelling mistakes we don't cache the
8280 * result.
8281 * Return the resulting attributes.
8282 */
8283 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008284hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008285 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008286 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008287{
8288 attrentry_T *char_aep = NULL;
8289 attrentry_T *spell_aep;
8290 attrentry_T new_en;
8291
8292 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008293 return prim_attr;
8294 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8295 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008296#ifdef FEAT_GUI
8297 if (gui.in_use)
8298 {
8299 if (char_attr > HL_ALL)
8300 char_aep = syn_gui_attr2entry(char_attr);
8301 if (char_aep != NULL)
8302 new_en = *char_aep;
8303 else
8304 {
8305 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008306 new_en.ae_u.gui.fg_color = INVALCOLOR;
8307 new_en.ae_u.gui.bg_color = INVALCOLOR;
8308 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008309 if (char_attr <= HL_ALL)
8310 new_en.ae_attr = char_attr;
8311 }
8312
Bram Moolenaar30abd282005-06-22 22:35:10 +00008313 if (prim_attr <= HL_ALL)
8314 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008315 else
8316 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008317 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008318 if (spell_aep != NULL)
8319 {
8320 new_en.ae_attr |= spell_aep->ae_attr;
8321 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8322 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8323 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8324 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8325 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8326 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8327 if (spell_aep->ae_u.gui.font != NOFONT)
8328 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8329# ifdef FEAT_XFONTSET
8330 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8331 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8332# endif
8333 }
8334 }
8335 return get_attr_entry(&gui_attr_table, &new_en);
8336 }
8337#endif
8338
8339 if (t_colors > 1)
8340 {
8341 if (char_attr > HL_ALL)
8342 char_aep = syn_cterm_attr2entry(char_attr);
8343 if (char_aep != NULL)
8344 new_en = *char_aep;
8345 else
8346 {
8347 vim_memset(&new_en, 0, sizeof(new_en));
8348 if (char_attr <= HL_ALL)
8349 new_en.ae_attr = char_attr;
8350 }
8351
Bram Moolenaar30abd282005-06-22 22:35:10 +00008352 if (prim_attr <= HL_ALL)
8353 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008354 else
8355 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008356 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008357 if (spell_aep != NULL)
8358 {
8359 new_en.ae_attr |= spell_aep->ae_attr;
8360 if (spell_aep->ae_u.cterm.fg_color > 0)
8361 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8362 if (spell_aep->ae_u.cterm.bg_color > 0)
8363 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8364 }
8365 }
8366 return get_attr_entry(&cterm_attr_table, &new_en);
8367 }
8368
8369 if (char_attr > HL_ALL)
8370 char_aep = syn_term_attr2entry(char_attr);
8371 if (char_aep != NULL)
8372 new_en = *char_aep;
8373 else
8374 {
8375 vim_memset(&new_en, 0, sizeof(new_en));
8376 if (char_attr <= HL_ALL)
8377 new_en.ae_attr = char_attr;
8378 }
8379
Bram Moolenaar30abd282005-06-22 22:35:10 +00008380 if (prim_attr <= HL_ALL)
8381 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008382 else
8383 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008384 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008385 if (spell_aep != NULL)
8386 {
8387 new_en.ae_attr |= spell_aep->ae_attr;
8388 if (spell_aep->ae_u.term.start != NULL)
8389 {
8390 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8391 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8392 }
8393 }
8394 }
8395 return get_attr_entry(&term_attr_table, &new_en);
8396}
8397#endif
8398
Bram Moolenaar071d4272004-06-13 20:20:40 +00008399#ifdef FEAT_GUI
8400
8401 attrentry_T *
8402syn_gui_attr2entry(attr)
8403 int attr;
8404{
8405 attr -= ATTR_OFF;
8406 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8407 return NULL;
8408 return &(GUI_ATTR_ENTRY(attr));
8409}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008410#endif /* FEAT_GUI */
8411
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008412/*
8413 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8414 * Only to be used when "attr" > HL_ALL.
8415 */
8416 int
8417syn_attr2attr(attr)
8418 int attr;
8419{
8420 attrentry_T *aep;
8421
8422#ifdef FEAT_GUI
8423 if (gui.in_use)
8424 aep = syn_gui_attr2entry(attr);
8425 else
8426#endif
8427 if (t_colors > 1)
8428 aep = syn_cterm_attr2entry(attr);
8429 else
8430 aep = syn_term_attr2entry(attr);
8431
8432 if (aep == NULL) /* highlighting not set */
8433 return 0;
8434 return aep->ae_attr;
8435}
8436
8437
Bram Moolenaar071d4272004-06-13 20:20:40 +00008438 attrentry_T *
8439syn_term_attr2entry(attr)
8440 int attr;
8441{
8442 attr -= ATTR_OFF;
8443 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8444 return NULL;
8445 return &(TERM_ATTR_ENTRY(attr));
8446}
8447
8448 attrentry_T *
8449syn_cterm_attr2entry(attr)
8450 int attr;
8451{
8452 attr -= ATTR_OFF;
8453 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8454 return NULL;
8455 return &(CTERM_ATTR_ENTRY(attr));
8456}
8457
8458#define LIST_ATTR 1
8459#define LIST_STRING 2
8460#define LIST_INT 3
8461
8462 static void
8463highlight_list_one(id)
8464 int id;
8465{
8466 struct hl_group *sgp;
8467 int didh = FALSE;
8468
8469 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8470
8471 didh = highlight_list_arg(id, didh, LIST_ATTR,
8472 sgp->sg_term, NULL, "term");
8473 didh = highlight_list_arg(id, didh, LIST_STRING,
8474 0, sgp->sg_start, "start");
8475 didh = highlight_list_arg(id, didh, LIST_STRING,
8476 0, sgp->sg_stop, "stop");
8477
8478 didh = highlight_list_arg(id, didh, LIST_ATTR,
8479 sgp->sg_cterm, NULL, "cterm");
8480 didh = highlight_list_arg(id, didh, LIST_INT,
8481 sgp->sg_cterm_fg, NULL, "ctermfg");
8482 didh = highlight_list_arg(id, didh, LIST_INT,
8483 sgp->sg_cterm_bg, NULL, "ctermbg");
8484
Bram Moolenaar61623362010-07-14 22:04:22 +02008485#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008486 didh = highlight_list_arg(id, didh, LIST_ATTR,
8487 sgp->sg_gui, NULL, "gui");
8488 didh = highlight_list_arg(id, didh, LIST_STRING,
8489 0, sgp->sg_gui_fg_name, "guifg");
8490 didh = highlight_list_arg(id, didh, LIST_STRING,
8491 0, sgp->sg_gui_bg_name, "guibg");
8492 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008493 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008494#endif
8495#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008496 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008497 0, sgp->sg_font_name, "font");
8498#endif
8499
Bram Moolenaar661b1822005-07-28 22:36:45 +00008500 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008501 {
8502 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008503 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008504 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8505 msg_putchar(' ');
8506 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8507 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008508
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008509 if (!didh)
8510 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008511#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008512 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008513 last_set_msg(sgp->sg_scriptID);
8514#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008515}
8516
8517 static int
8518highlight_list_arg(id, didh, type, iarg, sarg, name)
8519 int id;
8520 int didh;
8521 int type;
8522 int iarg;
8523 char_u *sarg;
8524 char *name;
8525{
8526 char_u buf[100];
8527 char_u *ts;
8528 int i;
8529
Bram Moolenaar661b1822005-07-28 22:36:45 +00008530 if (got_int)
8531 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008532 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8533 {
8534 ts = buf;
8535 if (type == LIST_INT)
8536 sprintf((char *)buf, "%d", iarg - 1);
8537 else if (type == LIST_STRING)
8538 ts = sarg;
8539 else /* type == LIST_ATTR */
8540 {
8541 buf[0] = NUL;
8542 for (i = 0; hl_attr_table[i] != 0; ++i)
8543 {
8544 if (iarg & hl_attr_table[i])
8545 {
8546 if (buf[0] != NUL)
8547 STRCAT(buf, ",");
8548 STRCAT(buf, hl_name_table[i]);
8549 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8550 }
8551 }
8552 }
8553
8554 (void)syn_list_header(didh,
8555 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8556 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008557 if (!got_int)
8558 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008559 if (*name != NUL)
8560 {
8561 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8562 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8563 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008564 msg_outtrans(ts);
8565 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008566 }
8567 return didh;
8568}
8569
8570#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8571/*
8572 * Return "1" if highlight group "id" has attribute "flag".
8573 * Return NULL otherwise.
8574 */
8575 char_u *
8576highlight_has_attr(id, flag, modec)
8577 int id;
8578 int flag;
8579 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8580{
8581 int attr;
8582
8583 if (id <= 0 || id > highlight_ga.ga_len)
8584 return NULL;
8585
Bram Moolenaar61623362010-07-14 22:04:22 +02008586#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008587 if (modec == 'g')
8588 attr = HL_TABLE()[id - 1].sg_gui;
8589 else
8590#endif
8591 if (modec == 'c')
8592 attr = HL_TABLE()[id - 1].sg_cterm;
8593 else
8594 attr = HL_TABLE()[id - 1].sg_term;
8595
8596 if (attr & flag)
8597 return (char_u *)"1";
8598 return NULL;
8599}
8600#endif
8601
8602#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8603/*
8604 * Return color name of highlight group "id".
8605 */
8606 char_u *
8607highlight_color(id, what, modec)
8608 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008609 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008610 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8611{
8612 static char_u name[20];
8613 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008614 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008615 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008616 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008617
8618 if (id <= 0 || id > highlight_ga.ga_len)
8619 return NULL;
8620
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008621 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008622 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008623 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008624 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008625 font = TRUE;
8626 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008627 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008628 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8629 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008630 if (modec == 'g')
8631 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008632# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008633 /* return font name */
8634 if (font)
8635 return HL_TABLE()[id - 1].sg_font_name;
8636
Bram Moolenaar071d4272004-06-13 20:20:40 +00008637 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008638 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008639 {
8640 guicolor_T color;
8641 long_u rgb;
8642 static char_u buf[10];
8643
8644 if (fg)
8645 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008646 else if (sp)
8647 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008648 else
8649 color = HL_TABLE()[id - 1].sg_gui_bg;
8650 if (color == INVALCOLOR)
8651 return NULL;
8652 rgb = gui_mch_get_rgb(color);
8653 sprintf((char *)buf, "#%02x%02x%02x",
8654 (unsigned)(rgb >> 16),
8655 (unsigned)(rgb >> 8) & 255,
8656 (unsigned)rgb & 255);
8657 return buf;
8658 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008659#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008660 if (fg)
8661 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008662 if (sp)
8663 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008664 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8665 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008666 if (font || sp)
8667 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008668 if (modec == 'c')
8669 {
8670 if (fg)
8671 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8672 else
8673 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8674 sprintf((char *)name, "%d", n);
8675 return name;
8676 }
8677 /* term doesn't have color */
8678 return NULL;
8679}
8680#endif
8681
8682#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8683 || defined(PROTO)
8684/*
8685 * Return color name of highlight group "id" as RGB value.
8686 */
8687 long_u
8688highlight_gui_color_rgb(id, fg)
8689 int id;
8690 int fg; /* TRUE = fg, FALSE = bg */
8691{
8692 guicolor_T color;
8693
8694 if (id <= 0 || id > highlight_ga.ga_len)
8695 return 0L;
8696
8697 if (fg)
8698 color = HL_TABLE()[id - 1].sg_gui_fg;
8699 else
8700 color = HL_TABLE()[id - 1].sg_gui_bg;
8701
8702 if (color == INVALCOLOR)
8703 return 0L;
8704
8705 return gui_mch_get_rgb(color);
8706}
8707#endif
8708
8709/*
8710 * Output the syntax list header.
8711 * Return TRUE when started a new line.
8712 */
8713 static int
8714syn_list_header(did_header, outlen, id)
8715 int did_header; /* did header already */
8716 int outlen; /* length of string that comes */
8717 int id; /* highlight group id */
8718{
8719 int endcol = 19;
8720 int newline = TRUE;
8721
8722 if (!did_header)
8723 {
8724 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008725 if (got_int)
8726 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008727 msg_outtrans(HL_TABLE()[id - 1].sg_name);
8728 endcol = 15;
8729 }
8730 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008731 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008732 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008733 if (got_int)
8734 return TRUE;
8735 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008736 else
8737 {
8738 if (msg_col >= endcol) /* wrap around is like starting a new line */
8739 newline = FALSE;
8740 }
8741
8742 if (msg_col >= endcol) /* output at least one space */
8743 endcol = msg_col + 1;
8744 if (Columns <= endcol) /* avoid hang for tiny window */
8745 endcol = Columns - 1;
8746
8747 msg_advance(endcol);
8748
8749 /* Show "xxx" with the attributes. */
8750 if (!did_header)
8751 {
8752 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8753 msg_putchar(' ');
8754 }
8755
8756 return newline;
8757}
8758
8759/*
8760 * Set the attribute numbers for a highlight group.
8761 * Called after one of the attributes has changed.
8762 */
8763 static void
8764set_hl_attr(idx)
8765 int idx; /* index in array */
8766{
8767 attrentry_T at_en;
8768 struct hl_group *sgp = HL_TABLE() + idx;
8769
8770 /* The "Normal" group doesn't need an attribute number */
8771 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8772 return;
8773
8774#ifdef FEAT_GUI
8775 /*
8776 * For the GUI mode: If there are other than "normal" highlighting
8777 * attributes, need to allocate an attr number.
8778 */
8779 if (sgp->sg_gui_fg == INVALCOLOR
8780 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008781 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008782 && sgp->sg_font == NOFONT
8783# ifdef FEAT_XFONTSET
8784 && sgp->sg_fontset == NOFONTSET
8785# endif
8786 )
8787 {
8788 sgp->sg_gui_attr = sgp->sg_gui;
8789 }
8790 else
8791 {
8792 at_en.ae_attr = sgp->sg_gui;
8793 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8794 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008795 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008796 at_en.ae_u.gui.font = sgp->sg_font;
8797# ifdef FEAT_XFONTSET
8798 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8799# endif
8800 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8801 }
8802#endif
8803 /*
8804 * For the term mode: If there are other than "normal" highlighting
8805 * attributes, need to allocate an attr number.
8806 */
8807 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8808 sgp->sg_term_attr = sgp->sg_term;
8809 else
8810 {
8811 at_en.ae_attr = sgp->sg_term;
8812 at_en.ae_u.term.start = sgp->sg_start;
8813 at_en.ae_u.term.stop = sgp->sg_stop;
8814 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8815 }
8816
8817 /*
8818 * For the color term mode: If there are other than "normal"
8819 * highlighting attributes, need to allocate an attr number.
8820 */
8821 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8822 sgp->sg_cterm_attr = sgp->sg_cterm;
8823 else
8824 {
8825 at_en.ae_attr = sgp->sg_cterm;
8826 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8827 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8828 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8829 }
8830}
8831
8832/*
8833 * Lookup a highlight group name and return it's ID.
8834 * If it is not found, 0 is returned.
8835 */
8836 int
8837syn_name2id(name)
8838 char_u *name;
8839{
8840 int i;
8841 char_u name_u[200];
8842
8843 /* Avoid using stricmp() too much, it's slow on some systems */
8844 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8845 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00008846 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008847 vim_strup(name_u);
8848 for (i = highlight_ga.ga_len; --i >= 0; )
8849 if (HL_TABLE()[i].sg_name_u != NULL
8850 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8851 break;
8852 return i + 1;
8853}
8854
8855#if defined(FEAT_EVAL) || defined(PROTO)
8856/*
8857 * Return TRUE if highlight group "name" exists.
8858 */
8859 int
8860highlight_exists(name)
8861 char_u *name;
8862{
8863 return (syn_name2id(name) > 0);
8864}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008865
8866# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
8867/*
8868 * Return the name of highlight group "id".
8869 * When not a valid ID return an empty string.
8870 */
8871 char_u *
8872syn_id2name(id)
8873 int id;
8874{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00008875 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008876 return (char_u *)"";
8877 return HL_TABLE()[id - 1].sg_name;
8878}
8879# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008880#endif
8881
8882/*
8883 * Like syn_name2id(), but take a pointer + length argument.
8884 */
8885 int
8886syn_namen2id(linep, len)
8887 char_u *linep;
8888 int len;
8889{
8890 char_u *name;
8891 int id = 0;
8892
8893 name = vim_strnsave(linep, len);
8894 if (name != NULL)
8895 {
8896 id = syn_name2id(name);
8897 vim_free(name);
8898 }
8899 return id;
8900}
8901
8902/*
8903 * Find highlight group name in the table and return it's ID.
8904 * The argument is a pointer to the name and the length of the name.
8905 * If it doesn't exist yet, a new entry is created.
8906 * Return 0 for failure.
8907 */
8908 int
8909syn_check_group(pp, len)
8910 char_u *pp;
8911 int len;
8912{
8913 int id;
8914 char_u *name;
8915
8916 name = vim_strnsave(pp, len);
8917 if (name == NULL)
8918 return 0;
8919
8920 id = syn_name2id(name);
8921 if (id == 0) /* doesn't exist yet */
8922 id = syn_add_group(name);
8923 else
8924 vim_free(name);
8925 return id;
8926}
8927
8928/*
8929 * Add new highlight group and return it's ID.
8930 * "name" must be an allocated string, it will be consumed.
8931 * Return 0 for failure.
8932 */
8933 static int
8934syn_add_group(name)
8935 char_u *name;
8936{
8937 char_u *p;
8938
8939 /* Check that the name is ASCII letters, digits and underscore. */
8940 for (p = name; *p != NUL; ++p)
8941 {
8942 if (!vim_isprintc(*p))
8943 {
8944 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008945 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008946 return 0;
8947 }
8948 else if (!ASCII_ISALNUM(*p) && *p != '_')
8949 {
8950 /* This is an error, but since there previously was no check only
8951 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008952 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008953 MSG(_("W18: Invalid character in group name"));
8954 break;
8955 }
8956 }
8957
8958 /*
8959 * First call for this growarray: init growing array.
8960 */
8961 if (highlight_ga.ga_data == NULL)
8962 {
8963 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8964 highlight_ga.ga_growsize = 10;
8965 }
8966
8967 /*
8968 * Make room for at least one other syntax_highlight entry.
8969 */
8970 if (ga_grow(&highlight_ga, 1) == FAIL)
8971 {
8972 vim_free(name);
8973 return 0;
8974 }
8975
8976 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8977 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8978 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8979#ifdef FEAT_GUI
8980 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8981 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008982 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008983#endif
8984 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008985
8986 return highlight_ga.ga_len; /* ID is index plus one */
8987}
8988
8989/*
8990 * When, just after calling syn_add_group(), an error is discovered, this
8991 * function deletes the new name.
8992 */
8993 static void
8994syn_unadd_group()
8995{
8996 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008997 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8998 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8999}
9000
9001/*
9002 * Translate a group ID to highlight attributes.
9003 */
9004 int
9005syn_id2attr(hl_id)
9006 int hl_id;
9007{
9008 int attr;
9009 struct hl_group *sgp;
9010
9011 hl_id = syn_get_final_id(hl_id);
9012 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9013
9014#ifdef FEAT_GUI
9015 /*
9016 * Only use GUI attr when the GUI is being used.
9017 */
9018 if (gui.in_use)
9019 attr = sgp->sg_gui_attr;
9020 else
9021#endif
9022 if (t_colors > 1)
9023 attr = sgp->sg_cterm_attr;
9024 else
9025 attr = sgp->sg_term_attr;
9026
9027 return attr;
9028}
9029
9030#ifdef FEAT_GUI
9031/*
9032 * Get the GUI colors and attributes for a group ID.
9033 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9034 */
9035 int
9036syn_id2colors(hl_id, fgp, bgp)
9037 int hl_id;
9038 guicolor_T *fgp;
9039 guicolor_T *bgp;
9040{
9041 struct hl_group *sgp;
9042
9043 hl_id = syn_get_final_id(hl_id);
9044 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9045
9046 *fgp = sgp->sg_gui_fg;
9047 *bgp = sgp->sg_gui_bg;
9048 return sgp->sg_gui;
9049}
9050#endif
9051
9052/*
9053 * Translate a group ID to the final group ID (following links).
9054 */
9055 int
9056syn_get_final_id(hl_id)
9057 int hl_id;
9058{
9059 int count;
9060 struct hl_group *sgp;
9061
9062 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9063 return 0; /* Can be called from eval!! */
9064
9065 /*
9066 * Follow links until there is no more.
9067 * Look out for loops! Break after 100 links.
9068 */
9069 for (count = 100; --count >= 0; )
9070 {
9071 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9072 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9073 break;
9074 hl_id = sgp->sg_link;
9075 }
9076
9077 return hl_id;
9078}
9079
9080#ifdef FEAT_GUI
9081/*
9082 * Call this function just after the GUI has started.
9083 * It finds the font and color handles for the highlighting groups.
9084 */
9085 void
9086highlight_gui_started()
9087{
9088 int idx;
9089
9090 /* First get the colors from the "Normal" and "Menu" group, if set */
9091 set_normal_colors();
9092
9093 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9094 gui_do_one_color(idx, FALSE, FALSE);
9095
9096 highlight_changed();
9097}
9098
9099 static void
9100gui_do_one_color(idx, do_menu, do_tooltip)
9101 int idx;
9102 int do_menu; /* TRUE: might set the menu font */
9103 int do_tooltip; /* TRUE: might set the tooltip font */
9104{
9105 int didit = FALSE;
9106
9107 if (HL_TABLE()[idx].sg_font_name != NULL)
9108 {
9109 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
9110 do_tooltip);
9111 didit = TRUE;
9112 }
9113 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9114 {
9115 HL_TABLE()[idx].sg_gui_fg =
9116 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9117 didit = TRUE;
9118 }
9119 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9120 {
9121 HL_TABLE()[idx].sg_gui_bg =
9122 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9123 didit = TRUE;
9124 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009125 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9126 {
9127 HL_TABLE()[idx].sg_gui_sp =
9128 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9129 didit = TRUE;
9130 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009131 if (didit) /* need to get a new attr number */
9132 set_hl_attr(idx);
9133}
9134
9135#endif
9136
9137/*
9138 * Translate the 'highlight' option into attributes in highlight_attr[] and
9139 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9140 * corresponding highlights to use on top of HLF_SNC is computed.
9141 * Called only when the 'highlight' option has been changed and upon first
9142 * screen redraw after any :highlight command.
9143 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9144 */
9145 int
9146highlight_changed()
9147{
9148 int hlf;
9149 int i;
9150 char_u *p;
9151 int attr;
9152 char_u *end;
9153 int id;
9154#ifdef USER_HIGHLIGHT
9155 char_u userhl[10];
9156# ifdef FEAT_STL_OPT
9157 int id_SNC = -1;
9158 int id_S = -1;
9159 int hlcnt;
9160# endif
9161#endif
9162 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9163
9164 need_highlight_changed = FALSE;
9165
9166 /*
9167 * Clear all attributes.
9168 */
9169 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9170 highlight_attr[hlf] = 0;
9171
9172 /*
9173 * First set all attributes to their default value.
9174 * Then use the attributes from the 'highlight' option.
9175 */
9176 for (i = 0; i < 2; ++i)
9177 {
9178 if (i)
9179 p = p_hl;
9180 else
9181 p = get_highlight_default();
9182 if (p == NULL) /* just in case */
9183 continue;
9184
9185 while (*p)
9186 {
9187 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9188 if (hl_flags[hlf] == *p)
9189 break;
9190 ++p;
9191 if (hlf == (int)HLF_COUNT || *p == NUL)
9192 return FAIL;
9193
9194 /*
9195 * Allow several hl_flags to be combined, like "bu" for
9196 * bold-underlined.
9197 */
9198 attr = 0;
9199 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9200 {
9201 if (vim_iswhite(*p)) /* ignore white space */
9202 continue;
9203
9204 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9205 return FAIL;
9206
9207 switch (*p)
9208 {
9209 case 'b': attr |= HL_BOLD;
9210 break;
9211 case 'i': attr |= HL_ITALIC;
9212 break;
9213 case '-':
9214 case 'n': /* no highlighting */
9215 break;
9216 case 'r': attr |= HL_INVERSE;
9217 break;
9218 case 's': attr |= HL_STANDOUT;
9219 break;
9220 case 'u': attr |= HL_UNDERLINE;
9221 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009222 case 'c': attr |= HL_UNDERCURL;
9223 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009224 case ':': ++p; /* highlight group name */
9225 if (attr || *p == NUL) /* no combinations */
9226 return FAIL;
9227 end = vim_strchr(p, ',');
9228 if (end == NULL)
9229 end = p + STRLEN(p);
9230 id = syn_check_group(p, (int)(end - p));
9231 if (id == 0)
9232 return FAIL;
9233 attr = syn_id2attr(id);
9234 p = end - 1;
9235#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9236 if (hlf == (int)HLF_SNC)
9237 id_SNC = syn_get_final_id(id);
9238 else if (hlf == (int)HLF_S)
9239 id_S = syn_get_final_id(id);
9240#endif
9241 break;
9242 default: return FAIL;
9243 }
9244 }
9245 highlight_attr[hlf] = attr;
9246
9247 p = skip_to_option_part(p); /* skip comma and spaces */
9248 }
9249 }
9250
9251#ifdef USER_HIGHLIGHT
9252 /* Setup the user highlights
9253 *
9254 * Temporarily utilize 10 more hl entries. Have to be in there
9255 * simultaneously in case of table overflows in get_attr_entry()
9256 */
9257# ifdef FEAT_STL_OPT
9258 if (ga_grow(&highlight_ga, 10) == FAIL)
9259 return FAIL;
9260 hlcnt = highlight_ga.ga_len;
9261 if (id_S == 0)
9262 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009263 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009264 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9265 id_S = hlcnt + 10;
9266 }
9267# endif
9268 for (i = 0; i < 9; i++)
9269 {
9270 sprintf((char *)userhl, "User%d", i + 1);
9271 id = syn_name2id(userhl);
9272 if (id == 0)
9273 {
9274 highlight_user[i] = 0;
9275# ifdef FEAT_STL_OPT
9276 highlight_stlnc[i] = 0;
9277# endif
9278 }
9279 else
9280 {
9281# ifdef FEAT_STL_OPT
9282 struct hl_group *hlt = HL_TABLE();
9283# endif
9284
9285 highlight_user[i] = syn_id2attr(id);
9286# ifdef FEAT_STL_OPT
9287 if (id_SNC == 0)
9288 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009289 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009290 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9291 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009292# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009293 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9294# endif
9295 }
9296 else
9297 mch_memmove(&hlt[hlcnt + i],
9298 &hlt[id_SNC - 1],
9299 sizeof(struct hl_group));
9300 hlt[hlcnt + i].sg_link = 0;
9301
9302 /* Apply difference between UserX and HLF_S to HLF_SNC */
9303 hlt[hlcnt + i].sg_term ^=
9304 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9305 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9306 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9307 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9308 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9309 hlt[hlcnt + i].sg_cterm ^=
9310 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9311 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9312 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9313 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9314 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009315# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009316 hlt[hlcnt + i].sg_gui ^=
9317 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009318# endif
9319# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009320 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9321 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9322 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9323 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009324 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9325 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009326 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9327 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9328# ifdef FEAT_XFONTSET
9329 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9330 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9331# endif
9332# endif
9333 highlight_ga.ga_len = hlcnt + i + 1;
9334 set_hl_attr(hlcnt + i); /* At long last we can apply */
9335 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9336# endif
9337 }
9338 }
9339# ifdef FEAT_STL_OPT
9340 highlight_ga.ga_len = hlcnt;
9341# endif
9342
9343#endif /* USER_HIGHLIGHT */
9344
9345 return OK;
9346}
9347
Bram Moolenaar4f688582007-07-24 12:34:30 +00009348#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009349
9350static void highlight_list __ARGS((void));
9351static void highlight_list_two __ARGS((int cnt, int attr));
9352
9353/*
9354 * Handle command line completion for :highlight command.
9355 */
9356 void
9357set_context_in_highlight_cmd(xp, arg)
9358 expand_T *xp;
9359 char_u *arg;
9360{
9361 char_u *p;
9362
9363 /* Default: expand group names */
9364 xp->xp_context = EXPAND_HIGHLIGHT;
9365 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009366 include_link = 2;
9367 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009368
9369 /* (part of) subcommand already typed */
9370 if (*arg != NUL)
9371 {
9372 p = skiptowhite(arg);
9373 if (*p != NUL) /* past "default" or group name */
9374 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009375 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009376 if (STRNCMP("default", arg, p - arg) == 0)
9377 {
9378 arg = skipwhite(p);
9379 xp->xp_pattern = arg;
9380 p = skiptowhite(arg);
9381 }
9382 if (*p != NUL) /* past group name */
9383 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009384 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009385 if (arg[1] == 'i' && arg[0] == 'N')
9386 highlight_list();
9387 if (STRNCMP("link", arg, p - arg) == 0
9388 || STRNCMP("clear", arg, p - arg) == 0)
9389 {
9390 xp->xp_pattern = skipwhite(p);
9391 p = skiptowhite(xp->xp_pattern);
9392 if (*p != NUL) /* past first group name */
9393 {
9394 xp->xp_pattern = skipwhite(p);
9395 p = skiptowhite(xp->xp_pattern);
9396 }
9397 }
9398 if (*p != NUL) /* past group name(s) */
9399 xp->xp_context = EXPAND_NOTHING;
9400 }
9401 }
9402 }
9403}
9404
9405/*
9406 * List highlighting matches in a nice way.
9407 */
9408 static void
9409highlight_list()
9410{
9411 int i;
9412
9413 for (i = 10; --i >= 0; )
9414 highlight_list_two(i, hl_attr(HLF_D));
9415 for (i = 40; --i >= 0; )
9416 highlight_list_two(99, 0);
9417}
9418
9419 static void
9420highlight_list_two(cnt, attr)
9421 int cnt;
9422 int attr;
9423{
9424 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
9425 msg_clr_eos();
9426 out_flush();
9427 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9428}
9429
9430#endif /* FEAT_CMDL_COMPL */
9431
9432#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9433 || defined(FEAT_SIGNS) || defined(PROTO)
9434/*
9435 * Function given to ExpandGeneric() to obtain the list of group names.
9436 * Also used for synIDattr() function.
9437 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009438 char_u *
9439get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009440 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009441 int idx;
9442{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009443#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009444 if (idx == highlight_ga.ga_len && include_none != 0)
9445 return (char_u *)"none";
9446 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009447 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009448 if (idx == highlight_ga.ga_len + include_none + include_default
9449 && include_link != 0)
9450 return (char_u *)"link";
9451 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9452 && include_link != 0)
9453 return (char_u *)"clear";
9454#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009455 if (idx < 0 || idx >= highlight_ga.ga_len)
9456 return NULL;
9457 return HL_TABLE()[idx].sg_name;
9458}
9459#endif
9460
Bram Moolenaar4f688582007-07-24 12:34:30 +00009461#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009462/*
9463 * Free all the highlight group fonts.
9464 * Used when quitting for systems which need it.
9465 */
9466 void
9467free_highlight_fonts()
9468{
9469 int idx;
9470
9471 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9472 {
9473 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9474 HL_TABLE()[idx].sg_font = NOFONT;
9475# ifdef FEAT_XFONTSET
9476 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9477 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9478# endif
9479 }
9480
9481 gui_mch_free_font(gui.norm_font);
9482# ifdef FEAT_XFONTSET
9483 gui_mch_free_fontset(gui.fontset);
9484# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009485# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009486 gui_mch_free_font(gui.bold_font);
9487 gui_mch_free_font(gui.ital_font);
9488 gui_mch_free_font(gui.boldital_font);
9489# endif
9490}
9491#endif
9492
9493/**************************************
9494 * End of Highlighting stuff *
9495 **************************************/