blob: 4476ad1487fe69cbe05d7f9b2995c9a4d8841e45 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
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
Bram Moolenaar071d4272004-06-13 20:20:40 +000016#if defined(FEAT_SYN_HL) || defined(PROTO)
17
18#define SYN_NAMELEN 50 /* maximum length of a syntax name */
19
20/* different types of offsets that are possible */
21#define SPO_MS_OFF 0 /* match start offset */
22#define SPO_ME_OFF 1 /* match end offset */
23#define SPO_HS_OFF 2 /* highl. start offset */
24#define SPO_HE_OFF 3 /* highl. end offset */
25#define SPO_RS_OFF 4 /* region start offset */
26#define SPO_RE_OFF 5 /* region end offset */
27#define SPO_LC_OFF 6 /* leading context offset */
28#define SPO_COUNT 7
29
30static char *(spo_name_tab[SPO_COUNT]) =
31 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
32
33/*
34 * The patterns that are being searched for are stored in a syn_pattern.
35 * A match item consists of one pattern.
36 * A start/end item consists of n start patterns and m end patterns.
37 * A start/skip/end item consists of n start patterns, one skip pattern and m
38 * end patterns.
39 * For the latter two, the patterns are always consecutive: start-skip-end.
40 *
41 * A character offset can be given for the matched text (_m_start and _m_end)
42 * and for the actually highlighted text (_h_start and _h_end).
Bram Moolenaar36f92302018-02-24 21:36:34 +010043 *
44 * Note that ordering of members is optimized to reduce padding.
Bram Moolenaar071d4272004-06-13 20:20:40 +000045 */
46typedef struct syn_pattern
47{
48 char sp_type; /* see SPTYPE_ defines below */
49 char sp_syncing; /* this item used for syncing */
Bram Moolenaar36f92302018-02-24 21:36:34 +010050 short sp_syn_match_id; /* highlight group ID of pattern */
51 short sp_off_flags; /* see below */
52 int sp_offsets[SPO_COUNT]; /* offsets */
Bram Moolenaar860cae12010-06-05 23:22:07 +020053 int sp_flags; /* see HL_ defines below */
54#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +020055 int sp_cchar; /* conceal substitute character */
Bram Moolenaar860cae12010-06-05 23:22:07 +020056#endif
Bram Moolenaar36f92302018-02-24 21:36:34 +010057 int sp_ic; /* ignore-case flag for sp_prog */
58 int sp_sync_idx; /* sync item index (syncing only) */
59 int sp_line_id; /* ID of last line where tried */
60 int sp_startcol; /* next match in sp_line_id line */
61 short *sp_cont_list; /* cont. group IDs, if non-zero */
62 short *sp_next_list; /* next group IDs, if non-zero */
Bram Moolenaar071d4272004-06-13 20:20:40 +000063 struct sp_syn sp_syn; /* struct passed to in_id_list() */
Bram Moolenaar071d4272004-06-13 20:20:40 +000064 char_u *sp_pattern; /* regexp to match, pattern */
65 regprog_T *sp_prog; /* regexp to match, program */
Bram Moolenaarf7512552013-06-06 14:55:19 +020066#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +020067 syn_time_T sp_time;
68#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000069} synpat_T;
70
71/* The sp_off_flags are computed like this:
72 * offset from the start of the matched text: (1 << SPO_XX_OFF)
73 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
74 * When both are present, only one is used.
75 */
76
77#define SPTYPE_MATCH 1 /* match keyword with this group ID */
78#define SPTYPE_START 2 /* match a regexp, start of item */
79#define SPTYPE_END 3 /* match a regexp, end of item */
80#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
81
Bram Moolenaar071d4272004-06-13 20:20:40 +000082
83#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
84
85#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
86
87/*
88 * Flags for b_syn_sync_flags:
89 */
90#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
91#define SF_MATCH 0x02 /* sync by matching a pattern */
92
93#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
94
Bram Moolenaar071d4272004-06-13 20:20:40 +000095#define MAXKEYWLEN 80 /* maximum length of a keyword */
96
97/*
98 * The attributes of the syntax item that has been recognized.
99 */
100static int current_attr = 0; /* attr of current syntax word */
101#ifdef FEAT_EVAL
102static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000103static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000104#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200105#ifdef FEAT_CONCEAL
106static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200107static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200108static int current_sub_char = 0;
109#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000110
Bram Moolenaar217ad922005-03-20 22:37:15 +0000111typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000112{
113 char_u *scl_name; /* syntax cluster name */
114 char_u *scl_name_u; /* uppercase of scl_name */
115 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000116} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000117
118/*
119 * Methods of combining two clusters
120 */
121#define CLUSTER_REPLACE 1 /* replace first list with second */
122#define CLUSTER_ADD 2 /* add second list to first */
123#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
124
Bram Moolenaar217ad922005-03-20 22:37:15 +0000125#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000126
127/*
128 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200129 * 0 - 19999 normal syntax groups
130 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
131 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
132 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
133 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000134 */
Bram Moolenaar2dfb3862011-04-02 15:12:50 +0200135#define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */
Bram Moolenaar42431a72011-04-01 14:44:59 +0200136#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */
137#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */
138#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */
139
Bram Moolenaar42431a72011-04-01 14:44:59 +0200140#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */
141#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000142
143/*
144 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
145 * expand_filename(). Most of the other syntax commands don't need it, so
146 * instead of passing it to them, we stow it here.
147 */
148static char_u **syn_cmdlinep;
149
150/*
151 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200152 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000153 * rules in each ":syn include"'d file.
154 */
155static int current_syn_inc_tag = 0;
156static int running_syn_inc_tag = 0;
157
158/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000159 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
160 * This avoids adding a pointer to the hashtable item.
161 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
162 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
163 * HI2KE() converts a hashitem pointer to a var pointer.
164 */
165static keyentry_T dumkey;
166#define KE2HIKEY(kp) ((kp)->keyword)
167#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
168#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
169
170/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000171 * To reduce the time spent in keepend(), remember at which level in the state
172 * stack the first item with "keepend" is present. When "-1", there is no
173 * "keepend" on the stack.
174 */
175static int keepend_level = -1;
176
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200177static char msg_no_items[] = N_("No Syntax items defined for this buffer");
178
Bram Moolenaar071d4272004-06-13 20:20:40 +0000179/*
180 * For the current state we need to remember more than just the idx.
181 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
182 * (The end positions have the column number of the next char)
183 */
184typedef struct state_item
185{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000186 int si_idx; /* index of syntax pattern or
187 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000188 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000189 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000190 int si_m_lnum; /* lnum of the match */
191 int si_m_startcol; /* starting column of the match */
192 lpos_T si_m_endpos; /* just after end posn of the match */
193 lpos_T si_h_startpos; /* start position of the highlighting */
194 lpos_T si_h_endpos; /* end position of the highlighting */
195 lpos_T si_eoe_pos; /* end position of end pattern */
196 int si_end_idx; /* group ID for end pattern or zero */
197 int si_ends; /* if match ends before si_m_endpos */
198 int si_attr; /* attributes in this state */
199 long si_flags; /* HL_HAS_EOL flag in this state, and
200 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200201#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200202 int si_seqnr; /* sequence number */
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200203 int si_cchar; /* substitution character for conceal */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200204#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000205 short *si_cont_list; /* list of contained groups */
206 short *si_next_list; /* nextgroup IDs after this item ends */
207 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
208 * pattern */
209} stateitem_T;
210
211#define KEYWORD_IDX -1 /* value of si_idx for keywords */
212#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
213 but contained groups */
214
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200215#ifdef FEAT_CONCEAL
Bram Moolenaare7154eb2015-03-21 21:46:13 +0100216static int next_seqnr = 1; /* value to use for si_seqnr */
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200217#endif
218
Bram Moolenaar071d4272004-06-13 20:20:40 +0000219/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000220 * Struct to reduce the number of arguments to get_syn_options(), it's used
221 * very often.
222 */
223typedef struct
224{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000225 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000226 int keyword; /* TRUE for ":syn keyword" */
227 int *sync_idx; /* syntax item for "grouphere" argument, NULL
228 if not allowed */
229 char has_cont_list; /* TRUE if "cont_list" can be used */
230 short *cont_list; /* group IDs for "contains" argument */
231 short *cont_in_list; /* group IDs for "containedin" argument */
232 short *next_list; /* group IDs for "nextgroup" argument */
233} syn_opt_arg_T;
234
235/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000236 * The next possible match in the current line for any pattern is remembered,
237 * to avoid having to try for a match in each column.
238 * If next_match_idx == -1, not tried (in this line) yet.
239 * If next_match_col == MAXCOL, no match found in this line.
240 * (All end positions have the column of the char after the end)
241 */
242static int next_match_col; /* column for start of next match */
243static lpos_T next_match_m_endpos; /* position for end of next match */
244static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
245static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
246static int next_match_idx; /* index of matched item */
247static long next_match_flags; /* flags for next match */
248static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
249static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
250static int next_match_end_idx; /* ID of group for end pattn or zero */
251static reg_extmatch_T *next_match_extmatch = NULL;
252
253/*
254 * A state stack is an array of integers or stateitem_T, stored in a
Bram Moolenaarf86db782018-10-25 13:31:37 +0200255 * garray_T. A state stack is invalid if its itemsize entry is zero.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000256 */
257#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
258#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
259
260/*
261 * The current state (within the line) of the recognition engine.
262 * When current_state.ga_itemsize is 0 the current state is invalid.
263 */
264static win_T *syn_win; /* current window for highlighting */
265static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200266static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200267#ifdef FEAT_RELTIME
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200268static proftime_T *syn_tm; /* timeout limit */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200269#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000270static linenr_T current_lnum = 0; /* lnum of current state */
271static colnr_T current_col = 0; /* column of current state */
272static int current_state_stored = 0; /* TRUE if stored current state
273 * after setting current_finished */
274static int current_finished = 0; /* current line has been finished */
275static garray_T current_state /* current stack of state_items */
276 = {0, 0, 0, 0, NULL};
277static short *current_next_list = NULL; /* when non-zero, nextgroup list */
278static int current_next_flags = 0; /* flags for current_next_list */
279static int current_line_id = 0; /* unique number for current line */
280
281#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
282
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100283static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100284static int syn_match_linecont(linenr_T lnum);
285static void syn_start_line(void);
286static void syn_update_ends(int startofline);
287static void syn_stack_alloc(void);
288static int syn_stack_cleanup(void);
289static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
290static synstate_T *syn_stack_find_entry(linenr_T lnum);
291static synstate_T *store_current_state(void);
292static void load_current_state(synstate_T *from);
293static void invalidate_current_state(void);
294static int syn_stack_equal(synstate_T *sp);
295static void validate_current_state(void);
296static int syn_finish_line(int syncing);
297static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
298static int did_match_already(int idx, garray_T *gap);
299static stateitem_T *push_next_match(stateitem_T *cur_si);
300static void check_state_ends(void);
301static void update_si_attr(int idx);
302static void check_keepend(void);
303static void update_si_end(stateitem_T *sip, int startcol, int force);
304static short *copy_id_list(short *list);
305static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
306static int push_current_state(int idx);
307static void pop_current_state(void);
Bram Moolenaarf7512552013-06-06 14:55:19 +0200308#ifdef FEAT_PROFILE
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100309static void syn_clear_time(syn_time_T *tt);
310static void syntime_clear(void);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100311static void syntime_report(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200312static int syn_time_on = FALSE;
313# define IF_SYN_TIME(p) (p)
314#else
315# define IF_SYN_TIME(p) NULL
316typedef int syn_time_T;
317#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000318
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100319static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
320static void find_endpos(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);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000321
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100322static void limit_pos(lpos_T *pos, lpos_T *limit);
323static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
324static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
325static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
326static char_u *syn_getcurline(void);
327static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
328static int check_keyword_id(char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100329static void syn_remove_pattern(synblock_T *block, int idx);
330static void syn_clear_pattern(synblock_T *block, int i);
331static void syn_clear_cluster(synblock_T *block, int i);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100332static void syn_clear_one(int id, int syncing);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100333static void syn_cmd_onoff(exarg_T *eap, char *name);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100334static void syn_lines_msg(void);
335static void syn_match_msg(void);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100336static void syn_list_one(int id, int syncing, int link_only);
337static void syn_list_cluster(int id);
338static void put_id_list(char_u *name, short *list, int attr);
339static void put_pattern(char *s, int c, synpat_T *spp, int attr);
340static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
341static void syn_clear_keyword(int id, hashtab_T *ht);
342static void clear_keywtab(hashtab_T *ht);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100343static int syn_scl_namen2id(char_u *linep, int len);
344static int syn_check_cluster(char_u *pp, int len);
345static int syn_add_cluster(char_u *name);
346static void init_syn_patterns(void);
347static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100348static int get_id_list(char_u **arg, int keylen, short **list, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100349static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000350
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200351#if defined(FEAT_RELTIME) || defined(PROTO)
352/*
353 * Set the timeout used for syntax highlighting.
354 * Use NULL to reset, no timeout.
355 */
356 void
357syn_set_timeout(proftime_T *tm)
358{
359 syn_tm = tm;
360}
361#endif
362
Bram Moolenaar071d4272004-06-13 20:20:40 +0000363/*
364 * Start the syntax recognition for a line. This function is normally called
365 * from the screen updating, once for each displayed line.
366 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
367 * it. Careful: curbuf and curwin are likely to point to another buffer and
368 * window.
369 */
370 void
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200371syntax_start(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000372{
373 synstate_T *p;
374 synstate_T *last_valid = NULL;
375 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000376 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000377 linenr_T parsed_lnum;
378 linenr_T first_stored;
379 int dist;
Bram Moolenaar79518e22017-02-17 16:31:35 +0100380 static varnumber_T changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000381
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200382#ifdef FEAT_CONCEAL
383 current_sub_char = NUL;
384#endif
385
Bram Moolenaar071d4272004-06-13 20:20:40 +0000386 /*
387 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000388 * Also do this when a change was made, the current state may be invalid
389 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000390 */
Bram Moolenaarb681be12016-03-31 23:02:16 +0200391 if (syn_block != wp->w_s
392 || syn_buf != wp->w_buffer
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100393 || changedtick != CHANGEDTICK(syn_buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000394 {
395 invalidate_current_state();
396 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200397 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000398 }
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100399 changedtick = CHANGEDTICK(syn_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000400 syn_win = wp;
401
402 /*
403 * Allocate syntax stack when needed.
404 */
405 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200406 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000407 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200408 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000409
410 /*
411 * If the state of the end of the previous line is useful, store it.
412 */
413 if (VALID_STATE(&current_state)
414 && current_lnum < lnum
415 && current_lnum < syn_buf->b_ml.ml_line_count)
416 {
417 (void)syn_finish_line(FALSE);
418 if (!current_state_stored)
419 {
420 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000421 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000422 }
423
424 /*
425 * If the current_lnum is now the same as "lnum", keep the current
426 * state (this happens very often!). Otherwise invalidate
427 * current_state and figure it out below.
428 */
429 if (current_lnum != lnum)
430 invalidate_current_state();
431 }
432 else
433 invalidate_current_state();
434
435 /*
436 * Try to synchronize from a saved state in b_sst_array[].
437 * Only do this if lnum is not before and not to far beyond a saved state.
438 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200439 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000440 {
441 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200442 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000443 {
444 if (p->sst_lnum > lnum)
445 break;
446 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
447 {
448 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200449 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000450 last_min_valid = p;
451 }
452 }
453 if (last_min_valid != NULL)
454 load_current_state(last_min_valid);
455 }
456
457 /*
458 * If "lnum" is before or far beyond a line with a saved state, need to
459 * re-synchronize.
460 */
461 if (INVALID_STATE(&current_state))
462 {
463 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200464 if (current_lnum == 1)
465 /* First line is always valid, no matter "minlines". */
466 first_stored = 1;
467 else
468 /* Need to parse "minlines" lines before state can be considered
469 * valid to store. */
470 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000471 }
472 else
473 first_stored = current_lnum;
474
475 /*
476 * Advance from the sync point or saved state until the current line.
477 * Save some entries for syncing with later on.
478 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200479 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000480 dist = 999999;
481 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200482 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000483 while (current_lnum < lnum)
484 {
485 syn_start_line();
486 (void)syn_finish_line(FALSE);
487 ++current_lnum;
488
489 /* If we parsed at least "minlines" lines or started at a valid
490 * state, the current state is considered valid. */
491 if (current_lnum >= first_stored)
492 {
493 /* Check if the saved state entry is for the current line and is
494 * equal to the current state. If so, then validate all saved
495 * states that depended on a change before the parsed line. */
496 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000497 prev = syn_stack_find_entry(current_lnum - 1);
498 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200499 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000500 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000501 sp = prev;
502 while (sp != NULL && sp->sst_lnum < current_lnum)
503 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000504 if (sp != NULL
505 && sp->sst_lnum == current_lnum
506 && syn_stack_equal(sp))
507 {
508 parsed_lnum = current_lnum;
509 prev = sp;
510 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
511 {
512 if (sp->sst_lnum <= lnum)
513 /* valid state before desired line, use this one */
514 prev = sp;
515 else if (sp->sst_change_lnum == 0)
516 /* past saved states depending on change, break here. */
517 break;
518 sp->sst_change_lnum = 0;
519 sp = sp->sst_next;
520 }
521 load_current_state(prev);
522 }
523 /* Store the state at this line when it's the first one, the line
524 * where we start parsing, or some distance from the previously
525 * saved state. But only when parsed at least 'minlines'. */
526 else if (prev == NULL
527 || current_lnum == lnum
528 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000529 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000530 }
531
532 /* This can take a long time: break when CTRL-C pressed. The current
533 * state will be wrong then. */
534 line_breakcheck();
535 if (got_int)
536 {
537 current_lnum = lnum;
538 break;
539 }
540 }
541
542 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000543}
544
545/*
546 * We cannot simply discard growarrays full of state_items or buf_states; we
547 * have to manually release their extmatch pointers first.
548 */
549 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100550clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000551{
552 int i;
553 garray_T *gap;
554
555 if (p->sst_stacksize > SST_FIX_STATES)
556 {
557 gap = &(p->sst_union.sst_ga);
558 for (i = 0; i < gap->ga_len; i++)
559 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
560 ga_clear(gap);
561 }
562 else
563 {
564 for (i = 0; i < p->sst_stacksize; i++)
565 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
566 }
567}
568
569/*
570 * Cleanup the current_state stack.
571 */
572 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100573clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000574{
575 int i;
576 stateitem_T *sip;
577
578 sip = (stateitem_T *)(current_state.ga_data);
579 for (i = 0; i < current_state.ga_len; i++)
580 unref_extmatch(sip[i].si_extmatch);
581 ga_clear(&current_state);
582}
583
584/*
585 * Try to find a synchronisation point for line "lnum".
586 *
587 * This sets current_lnum and the current state. One of three methods is
588 * used:
589 * 1. Search backwards for the end of a C-comment.
590 * 2. Search backwards for given sync patterns.
591 * 3. Simply start on a given number of lines above "lnum".
592 */
593 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100594syn_sync(
595 win_T *wp,
596 linenr_T start_lnum,
597 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000598{
599 buf_T *curbuf_save;
600 win_T *curwin_save;
601 pos_T cursor_save;
602 int idx;
603 linenr_T lnum;
604 linenr_T end_lnum;
605 linenr_T break_lnum;
606 int had_sync_point;
607 stateitem_T *cur_si;
608 synpat_T *spp;
609 char_u *line;
610 int found_flags = 0;
611 int found_match_idx = 0;
612 linenr_T found_current_lnum = 0;
613 int found_current_col= 0;
614 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000615 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000616
617 /*
618 * Clear any current state that might be hanging around.
619 */
620 invalidate_current_state();
621
622 /*
623 * Start at least "minlines" back. Default starting point for parsing is
624 * there.
625 * Start further back, to avoid that scrolling backwards will result in
626 * resyncing for every line. Now it resyncs only one out of N lines,
627 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
628 * Watch out for overflow when minlines is MAXLNUM.
629 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200630 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000631 start_lnum = 1;
632 else
633 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200634 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000635 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200636 else if (syn_block->b_syn_sync_minlines < 10)
637 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000638 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200639 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
640 if (syn_block->b_syn_sync_maxlines != 0
641 && lnum > syn_block->b_syn_sync_maxlines)
642 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000643 if (lnum >= start_lnum)
644 start_lnum = 1;
645 else
646 start_lnum -= lnum;
647 }
648 current_lnum = start_lnum;
649
650 /*
651 * 1. Search backwards for the end of a C-style comment.
652 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200653 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000654 {
655 /* Need to make syn_buf the current buffer for a moment, to be able to
656 * use find_start_comment(). */
657 curwin_save = curwin;
658 curwin = wp;
659 curbuf_save = curbuf;
660 curbuf = syn_buf;
661
662 /*
663 * Skip lines that end in a backslash.
664 */
665 for ( ; start_lnum > 1; --start_lnum)
666 {
667 line = ml_get(start_lnum - 1);
668 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
669 break;
670 }
671 current_lnum = start_lnum;
672
673 /* set cursor to start of search */
674 cursor_save = wp->w_cursor;
675 wp->w_cursor.lnum = start_lnum;
676 wp->w_cursor.col = 0;
677
678 /*
679 * If the line is inside a comment, need to find the syntax item that
680 * defines the comment.
681 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
682 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200683 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000684 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200685 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
686 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
687 == syn_block->b_syn_sync_id
688 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000689 {
690 validate_current_state();
691 if (push_current_state(idx) == OK)
692 update_si_attr(current_state.ga_len - 1);
693 break;
694 }
695 }
696
697 /* restore cursor and buffer */
698 wp->w_cursor = cursor_save;
699 curwin = curwin_save;
700 curbuf = curbuf_save;
701 }
702
703 /*
704 * 2. Search backwards for given sync patterns.
705 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200706 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000707 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200708 if (syn_block->b_syn_sync_maxlines != 0
709 && start_lnum > syn_block->b_syn_sync_maxlines)
710 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000711 else
712 break_lnum = 0;
713
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000714 found_m_endpos.lnum = 0;
715 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000716 end_lnum = start_lnum;
717 lnum = start_lnum;
718 while (--lnum > break_lnum)
719 {
720 /* This can take a long time: break when CTRL-C pressed. */
721 line_breakcheck();
722 if (got_int)
723 {
724 invalidate_current_state();
725 current_lnum = start_lnum;
726 break;
727 }
728
729 /* Check if we have run into a valid saved state stack now. */
730 if (last_valid != NULL && lnum == last_valid->sst_lnum)
731 {
732 load_current_state(last_valid);
733 break;
734 }
735
736 /*
737 * Check if the previous line has the line-continuation pattern.
738 */
739 if (lnum > 1 && syn_match_linecont(lnum - 1))
740 continue;
741
742 /*
743 * Start with nothing on the state stack
744 */
745 validate_current_state();
746
747 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
748 {
749 syn_start_line();
750 for (;;)
751 {
752 had_sync_point = syn_finish_line(TRUE);
753 /*
754 * When a sync point has been found, remember where, and
755 * continue to look for another one, further on in the line.
756 */
757 if (had_sync_point && current_state.ga_len)
758 {
759 cur_si = &CUR_STATE(current_state.ga_len - 1);
760 if (cur_si->si_m_endpos.lnum > start_lnum)
761 {
762 /* ignore match that goes to after where started */
763 current_lnum = end_lnum;
764 break;
765 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000766 if (cur_si->si_idx < 0)
767 {
768 /* Cannot happen? */
769 found_flags = 0;
770 found_match_idx = KEYWORD_IDX;
771 }
772 else
773 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200774 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000775 found_flags = spp->sp_flags;
776 found_match_idx = spp->sp_sync_idx;
777 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000778 found_current_lnum = current_lnum;
779 found_current_col = current_col;
780 found_m_endpos = cur_si->si_m_endpos;
781 /*
782 * Continue after the match (be aware of a zero-length
783 * match).
784 */
785 if (found_m_endpos.lnum > current_lnum)
786 {
787 current_lnum = found_m_endpos.lnum;
788 current_col = found_m_endpos.col;
789 if (current_lnum >= end_lnum)
790 break;
791 }
792 else if (found_m_endpos.col > current_col)
793 current_col = found_m_endpos.col;
794 else
795 ++current_col;
796
797 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000798 * an item that ends here, need to do that now. Be
799 * careful not to go past the NUL. */
800 prev_current_col = current_col;
801 if (syn_getcurline()[current_col] != NUL)
802 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000803 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000804 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000805 }
806 else
807 break;
808 }
809 }
810
811 /*
812 * If a sync point was encountered, break here.
813 */
814 if (found_flags)
815 {
816 /*
817 * Put the item that was specified by the sync point on the
818 * state stack. If there was no item specified, make the
819 * state stack empty.
820 */
821 clear_current_state();
822 if (found_match_idx >= 0
823 && push_current_state(found_match_idx) == OK)
824 update_si_attr(current_state.ga_len - 1);
825
826 /*
827 * When using "grouphere", continue from the sync point
828 * match, until the end of the line. Parsing starts at
829 * the next line.
830 * For "groupthere" the parsing starts at start_lnum.
831 */
832 if (found_flags & HL_SYNC_HERE)
833 {
834 if (current_state.ga_len)
835 {
836 cur_si = &CUR_STATE(current_state.ga_len - 1);
837 cur_si->si_h_startpos.lnum = found_current_lnum;
838 cur_si->si_h_startpos.col = found_current_col;
839 update_si_end(cur_si, (int)current_col, TRUE);
840 check_keepend();
841 }
842 current_col = found_m_endpos.col;
843 current_lnum = found_m_endpos.lnum;
844 (void)syn_finish_line(FALSE);
845 ++current_lnum;
846 }
847 else
848 current_lnum = start_lnum;
849
850 break;
851 }
852
853 end_lnum = lnum;
854 invalidate_current_state();
855 }
856
857 /* Ran into start of the file or exceeded maximum number of lines */
858 if (lnum <= break_lnum)
859 {
860 invalidate_current_state();
861 current_lnum = break_lnum + 1;
862 }
863 }
864
865 validate_current_state();
866}
867
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100868 static void
869save_chartab(char_u *chartab)
870{
871 if (syn_block->b_syn_isk != empty_option)
872 {
873 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
874 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
875 (size_t)32);
876 }
877}
878
879 static void
880restore_chartab(char_u *chartab)
881{
882 if (syn_win->w_s->b_syn_isk != empty_option)
883 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
884}
885
Bram Moolenaar071d4272004-06-13 20:20:40 +0000886/*
887 * Return TRUE if the line-continuation pattern matches in line "lnum".
888 */
889 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100890syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000891{
892 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100893 int r;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100894 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000895
Bram Moolenaar860cae12010-06-05 23:22:07 +0200896 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000897 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100898 /* use syntax iskeyword option */
899 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200900 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
901 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100902 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200903 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100904 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100905 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100906 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000907 }
908 return FALSE;
909}
910
911/*
912 * Prepare the current state for the start of a line.
913 */
914 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100915syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000916{
917 current_finished = FALSE;
918 current_col = 0;
919
920 /*
921 * Need to update the end of a start/skip/end that continues from the
922 * previous line and regions that have "keepend".
923 */
924 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +0200925 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000926 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +0200927 check_state_ends();
928 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000929
930 next_match_idx = -1;
931 ++current_line_id;
Bram Moolenaarea20de82017-06-24 22:52:24 +0200932#ifdef FEAT_CONCEAL
Bram Moolenaarcc0750d2017-06-24 22:29:24 +0200933 next_seqnr = 1;
Bram Moolenaarea20de82017-06-24 22:52:24 +0200934#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000935}
936
937/*
938 * Check for items in the stack that need their end updated.
939 * When "startofline" is TRUE the last item is always updated.
940 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
941 */
942 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100943syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000944{
945 stateitem_T *cur_si;
946 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000947 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000948
949 if (startofline)
950 {
951 /* Check for a match carried over from a previous line with a
952 * contained region. The match ends as soon as the region ends. */
953 for (i = 0; i < current_state.ga_len; ++i)
954 {
955 cur_si = &CUR_STATE(i);
956 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +0200957 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +0000958 == SPTYPE_MATCH
959 && cur_si->si_m_endpos.lnum < current_lnum)
960 {
961 cur_si->si_flags |= HL_MATCHCONT;
962 cur_si->si_m_endpos.lnum = 0;
963 cur_si->si_m_endpos.col = 0;
964 cur_si->si_h_endpos = cur_si->si_m_endpos;
965 cur_si->si_ends = TRUE;
966 }
967 }
968 }
969
970 /*
971 * Need to update the end of a start/skip/end that continues from the
972 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000973 * influence contained items. If we've just removed "extend"
974 * (startofline == 0) then we should update ends of normal regions
975 * contained inside "keepend" because "extend" could have extended
976 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000977 * Then check for items ending in column 0.
978 */
979 i = current_state.ga_len - 1;
980 if (keepend_level >= 0)
981 for ( ; i > keepend_level; --i)
982 if (CUR_STATE(i).si_flags & HL_EXTEND)
983 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000984
985 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000986 for ( ; i < current_state.ga_len; ++i)
987 {
988 cur_si = &CUR_STATE(i);
989 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000990 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000991 || (i == current_state.ga_len - 1 && startofline))
992 {
993 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
994 cur_si->si_h_startpos.lnum = current_lnum;
995
996 if (!(cur_si->si_flags & HL_MATCHCONT))
997 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000998
999 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1000 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001001 }
1002 }
1003 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001004}
1005
1006/****************************************
1007 * Handling of the state stack cache.
1008 */
1009
1010/*
1011 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1012 *
1013 * To speed up syntax highlighting, the state stack for the start of some
1014 * lines is cached. These entries can be used to start parsing at that point.
1015 *
1016 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1017 * valid entries. b_sst_first points to the first one, then follow sst_next.
1018 * The entries are sorted on line number. The first entry is often for line 2
1019 * (line 1 always starts with an empty stack).
1020 * There is also a list for free entries. This construction is used to avoid
1021 * having to allocate and free memory blocks too often.
1022 *
1023 * When making changes to the buffer, this is logged in b_mod_*. When calling
1024 * update_screen() to update the display, it will call
1025 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1026 * entries. The entries which are inside the changed area are removed,
1027 * because they must be recomputed. Entries below the changed have their line
1028 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1029 * set to indicate that a check must be made if the changed lines would change
1030 * the cached entry.
1031 *
1032 * When later displaying lines, an entry is stored for each line. Displayed
1033 * lines are likely to be displayed again, in which case the state at the
1034 * start of the line is needed.
1035 * For not displayed lines, an entry is stored for every so many lines. These
1036 * entries will be used e.g., when scrolling backwards. The distance between
1037 * entries depends on the number of lines in the buffer. For small buffers
1038 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1039 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1040 */
1041
Bram Moolenaar860cae12010-06-05 23:22:07 +02001042 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001043syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001044{
1045 synstate_T *p;
1046
1047 if (block->b_sst_array != NULL)
1048 {
1049 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1050 clear_syn_state(p);
Bram Moolenaard23a8232018-02-10 18:45:26 +01001051 VIM_CLEAR(block->b_sst_array);
Bram Moolenaar95892c22018-09-28 22:26:54 +02001052 block->b_sst_first = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001053 block->b_sst_len = 0;
1054 }
1055}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001056/*
1057 * Free b_sst_array[] for buffer "buf".
1058 * Used when syntax items changed to force resyncing everywhere.
1059 */
1060 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001061syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001062{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001063#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001064 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001065#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001066
Bram Moolenaar860cae12010-06-05 23:22:07 +02001067 syn_stack_free_block(block);
1068
Bram Moolenaar071d4272004-06-13 20:20:40 +00001069#ifdef FEAT_FOLDING
1070 /* When using "syntax" fold method, must update all folds. */
1071 FOR_ALL_WINDOWS(wp)
1072 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001073 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001074 foldUpdateAll(wp);
1075 }
1076#endif
1077}
1078
1079/*
1080 * Allocate the syntax state stack for syn_buf when needed.
1081 * If the number of entries in b_sst_array[] is much too big or a bit too
1082 * small, reallocate it.
1083 * Also used to allocate b_sst_array[] for the first time.
1084 */
1085 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001086syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001087{
1088 long len;
1089 synstate_T *to, *from;
1090 synstate_T *sstp;
1091
1092 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1093 if (len < SST_MIN_ENTRIES)
1094 len = SST_MIN_ENTRIES;
1095 else if (len > SST_MAX_ENTRIES)
1096 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001097 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001098 {
1099 /* Allocate 50% too much, to avoid reallocating too often. */
1100 len = syn_buf->b_ml.ml_line_count;
1101 len = (len + len / 2) / SST_DIST + Rows * 2;
1102 if (len < SST_MIN_ENTRIES)
1103 len = SST_MIN_ENTRIES;
1104 else if (len > SST_MAX_ENTRIES)
1105 len = SST_MAX_ENTRIES;
1106
Bram Moolenaar860cae12010-06-05 23:22:07 +02001107 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001108 {
1109 /* When shrinking the array, cleanup the existing stack.
1110 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001111 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001112 && syn_stack_cleanup())
1113 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001114 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1115 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001116 }
1117
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001118 sstp = ALLOC_CLEAR_MULT(synstate_T, len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001119 if (sstp == NULL) /* out of memory! */
1120 return;
1121
1122 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001123 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001124 {
1125 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001126 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001127 from = from->sst_next)
1128 {
1129 ++to;
1130 *to = *from;
1131 to->sst_next = to + 1;
1132 }
1133 }
1134 if (to != sstp - 1)
1135 {
1136 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001137 syn_block->b_sst_first = sstp;
1138 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001139 }
1140 else
1141 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001142 syn_block->b_sst_first = NULL;
1143 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001144 }
1145
1146 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001147 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001148 while (++to < sstp + len)
1149 to->sst_next = to + 1;
1150 (sstp + len - 1)->sst_next = NULL;
1151
Bram Moolenaar860cae12010-06-05 23:22:07 +02001152 vim_free(syn_block->b_sst_array);
1153 syn_block->b_sst_array = sstp;
1154 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001155 }
1156}
1157
1158/*
1159 * Check for changes in a buffer to affect stored syntax states. Uses the
1160 * b_mod_* fields.
1161 * Called from update_screen(), before screen is being updated, once for each
1162 * displayed buffer.
1163 */
1164 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001165syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001166{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001167 win_T *wp;
1168
1169 syn_stack_apply_changes_block(&buf->b_s, buf);
1170
1171 FOR_ALL_WINDOWS(wp)
1172 {
1173 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1174 syn_stack_apply_changes_block(wp->w_s, buf);
1175 }
1176}
1177
1178 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001179syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001180{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001181 synstate_T *p, *prev, *np;
1182 linenr_T n;
1183
Bram Moolenaar071d4272004-06-13 20:20:40 +00001184 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001185 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001186 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001187 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001188 {
1189 n = p->sst_lnum + buf->b_mod_xlines;
1190 if (n <= buf->b_mod_bot)
1191 {
1192 /* this state is inside the changed area, remove it */
1193 np = p->sst_next;
1194 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001195 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001196 else
1197 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001198 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001199 p = np;
1200 continue;
1201 }
1202 /* This state is below the changed area. Remember the line
1203 * that needs to be parsed before this entry can be made valid
1204 * again. */
1205 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1206 {
1207 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1208 p->sst_change_lnum += buf->b_mod_xlines;
1209 else
1210 p->sst_change_lnum = buf->b_mod_top;
1211 }
1212 if (p->sst_change_lnum == 0
1213 || p->sst_change_lnum < buf->b_mod_bot)
1214 p->sst_change_lnum = buf->b_mod_bot;
1215
1216 p->sst_lnum = n;
1217 }
1218 prev = p;
1219 p = p->sst_next;
1220 }
1221}
1222
1223/*
1224 * Reduce the number of entries in the state stack for syn_buf.
1225 * Returns TRUE if at least one entry was freed.
1226 */
1227 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001228syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001229{
1230 synstate_T *p, *prev;
1231 disptick_T tick;
1232 int above;
1233 int dist;
1234 int retval = FALSE;
1235
Bram Moolenaar95892c22018-09-28 22:26:54 +02001236 if (syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001237 return retval;
1238
1239 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001240 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001241 dist = 999999;
1242 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001243 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244
1245 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001246 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001247 * be removed. Set "above" when the "tick" for the oldest entry is above
1248 * "b_sst_lasttick" (the display tick wraps around).
1249 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001250 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001251 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001252 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001253 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1254 {
1255 if (prev->sst_lnum + dist > p->sst_lnum)
1256 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001257 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001258 {
1259 if (!above || p->sst_tick < tick)
1260 tick = p->sst_tick;
1261 above = TRUE;
1262 }
1263 else if (!above && p->sst_tick < tick)
1264 tick = p->sst_tick;
1265 }
1266 }
1267
1268 /*
1269 * Go through the list to make the entries for the oldest tick at an
1270 * interval of several lines.
1271 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001272 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001273 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1274 {
1275 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1276 {
1277 /* Move this entry from used list to free list */
1278 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001279 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001280 p = prev;
1281 retval = TRUE;
1282 }
1283 }
1284 return retval;
1285}
1286
1287/*
1288 * Free the allocated memory for a syn_state item.
1289 * Move the entry into the free list.
1290 */
1291 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001292syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001293{
1294 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001295 p->sst_next = block->b_sst_firstfree;
1296 block->b_sst_firstfree = p;
1297 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001298}
1299
1300/*
1301 * Find an entry in the list of state stacks at or before "lnum".
1302 * Returns NULL when there is no entry or the first entry is after "lnum".
1303 */
1304 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001305syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001306{
1307 synstate_T *p, *prev;
1308
1309 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001310 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001311 {
1312 if (p->sst_lnum == lnum)
1313 return p;
1314 if (p->sst_lnum > lnum)
1315 break;
1316 }
1317 return prev;
1318}
1319
1320/*
1321 * Try saving the current state in b_sst_array[].
1322 * The current state must be valid for the start of the current_lnum line!
1323 */
1324 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001325store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001326{
1327 int i;
1328 synstate_T *p;
1329 bufstate_T *bp;
1330 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001331 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001332
1333 /*
1334 * If the current state contains a start or end pattern that continues
1335 * from the previous line, we can't use it. Don't store it then.
1336 */
1337 for (i = current_state.ga_len - 1; i >= 0; --i)
1338 {
1339 cur_si = &CUR_STATE(i);
1340 if (cur_si->si_h_startpos.lnum >= current_lnum
1341 || cur_si->si_m_endpos.lnum >= current_lnum
1342 || cur_si->si_h_endpos.lnum >= current_lnum
1343 || (cur_si->si_end_idx
1344 && cur_si->si_eoe_pos.lnum >= current_lnum))
1345 break;
1346 }
1347 if (i >= 0)
1348 {
1349 if (sp != NULL)
1350 {
1351 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001352 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001353 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001354 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001355 else
1356 {
1357 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001358 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001359 if (p->sst_next == sp)
1360 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001361 if (p != NULL) /* just in case */
1362 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001363 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001364 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001365 sp = NULL;
1366 }
1367 }
1368 else if (sp == NULL || sp->sst_lnum != current_lnum)
1369 {
1370 /*
1371 * Add a new entry
1372 */
1373 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001374 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001375 {
1376 (void)syn_stack_cleanup();
1377 /* "sp" may have been moved to the freelist now */
1378 sp = syn_stack_find_entry(current_lnum);
1379 }
1380 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001381 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001382 sp = NULL;
1383 else
1384 {
1385 /* Take the first item from the free list and put it in the used
1386 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001387 p = syn_block->b_sst_firstfree;
1388 syn_block->b_sst_firstfree = p->sst_next;
1389 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001390 if (sp == NULL)
1391 {
1392 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001393 p->sst_next = syn_block->b_sst_first;
1394 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001395 }
1396 else
1397 {
1398 /* insert in list after *sp */
1399 p->sst_next = sp->sst_next;
1400 sp->sst_next = p;
1401 }
1402 sp = p;
1403 sp->sst_stacksize = 0;
1404 sp->sst_lnum = current_lnum;
1405 }
1406 }
1407 if (sp != NULL)
1408 {
1409 /* When overwriting an existing state stack, clear it first */
1410 clear_syn_state(sp);
1411 sp->sst_stacksize = current_state.ga_len;
1412 if (current_state.ga_len > SST_FIX_STATES)
1413 {
1414 /* Need to clear it, might be something remaining from when the
1415 * length was less than SST_FIX_STATES. */
1416 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1417 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1418 sp->sst_stacksize = 0;
1419 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001420 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001421 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1422 }
1423 else
1424 bp = sp->sst_union.sst_stack;
1425 for (i = 0; i < sp->sst_stacksize; ++i)
1426 {
1427 bp[i].bs_idx = CUR_STATE(i).si_idx;
1428 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001429#ifdef FEAT_CONCEAL
1430 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1431 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1432#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001433 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1434 }
1435 sp->sst_next_flags = current_next_flags;
1436 sp->sst_next_list = current_next_list;
1437 sp->sst_tick = display_tick;
1438 sp->sst_change_lnum = 0;
1439 }
1440 current_state_stored = TRUE;
1441 return sp;
1442}
1443
1444/*
1445 * Copy a state stack from "from" in b_sst_array[] to current_state;
1446 */
1447 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001448load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001449{
1450 int i;
1451 bufstate_T *bp;
1452
1453 clear_current_state();
1454 validate_current_state();
1455 keepend_level = -1;
1456 if (from->sst_stacksize
1457 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1458 {
1459 if (from->sst_stacksize > SST_FIX_STATES)
1460 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1461 else
1462 bp = from->sst_union.sst_stack;
1463 for (i = 0; i < from->sst_stacksize; ++i)
1464 {
1465 CUR_STATE(i).si_idx = bp[i].bs_idx;
1466 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001467#ifdef FEAT_CONCEAL
1468 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1469 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1470#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001471 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1472 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1473 keepend_level = i;
1474 CUR_STATE(i).si_ends = FALSE;
1475 CUR_STATE(i).si_m_lnum = 0;
1476 if (CUR_STATE(i).si_idx >= 0)
1477 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001478 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001479 else
1480 CUR_STATE(i).si_next_list = NULL;
1481 update_si_attr(i);
1482 }
1483 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001484 }
1485 current_next_list = from->sst_next_list;
1486 current_next_flags = from->sst_next_flags;
1487 current_lnum = from->sst_lnum;
1488}
1489
1490/*
1491 * Compare saved state stack "*sp" with the current state.
1492 * Return TRUE when they are equal.
1493 */
1494 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001495syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001496{
1497 int i, j;
1498 bufstate_T *bp;
1499 reg_extmatch_T *six, *bsx;
1500
1501 /* First a quick check if the stacks have the same size end nextlist. */
1502 if (sp->sst_stacksize == current_state.ga_len
1503 && sp->sst_next_list == current_next_list)
1504 {
1505 /* Need to compare all states on both stacks. */
1506 if (sp->sst_stacksize > SST_FIX_STATES)
1507 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1508 else
1509 bp = sp->sst_union.sst_stack;
1510
1511 for (i = current_state.ga_len; --i >= 0; )
1512 {
1513 /* If the item has another index the state is different. */
1514 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1515 break;
1516 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1517 {
1518 /* When the extmatch pointers are different, the strings in
1519 * them can still be the same. Check if the extmatch
1520 * references are equal. */
1521 bsx = bp[i].bs_extmatch;
1522 six = CUR_STATE(i).si_extmatch;
1523 /* If one of the extmatch pointers is NULL the states are
1524 * different. */
1525 if (bsx == NULL || six == NULL)
1526 break;
1527 for (j = 0; j < NSUBEXP; ++j)
1528 {
1529 /* Check each referenced match string. They must all be
1530 * equal. */
1531 if (bsx->matches[j] != six->matches[j])
1532 {
1533 /* If the pointer is different it can still be the
1534 * same text. Compare the strings, ignore case when
1535 * the start item has the sp_ic flag set. */
1536 if (bsx->matches[j] == NULL
1537 || six->matches[j] == NULL)
1538 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001539 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001540 ? MB_STRICMP(bsx->matches[j],
1541 six->matches[j]) != 0
1542 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1543 break;
1544 }
1545 }
1546 if (j != NSUBEXP)
1547 break;
1548 }
1549 }
1550 if (i < 0)
1551 return TRUE;
1552 }
1553 return FALSE;
1554}
1555
1556/*
1557 * We stop parsing syntax above line "lnum". If the stored state at or below
1558 * this line depended on a change before it, it now depends on the line below
1559 * the last parsed line.
1560 * The window looks like this:
1561 * line which changed
1562 * displayed line
1563 * displayed line
1564 * lnum -> line below window
1565 */
1566 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001567syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001568{
1569 synstate_T *sp;
1570
1571 sp = syn_stack_find_entry(lnum);
1572 if (sp != NULL && sp->sst_lnum < lnum)
1573 sp = sp->sst_next;
1574
1575 if (sp != NULL && sp->sst_change_lnum != 0)
1576 sp->sst_change_lnum = lnum;
1577}
1578
1579/*
1580 * End of handling of the state stack.
1581 ****************************************/
1582
1583 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001584invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001585{
1586 clear_current_state();
1587 current_state.ga_itemsize = 0; /* mark current_state invalid */
1588 current_next_list = NULL;
1589 keepend_level = -1;
1590}
1591
1592 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001593validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001594{
1595 current_state.ga_itemsize = sizeof(stateitem_T);
1596 current_state.ga_growsize = 3;
1597}
1598
1599/*
1600 * Return TRUE if the syntax at start of lnum changed since last time.
1601 * This will only be called just after get_syntax_attr() for the previous
1602 * line, to check if the next line needs to be redrawn too.
1603 */
1604 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001605syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001606{
1607 int retval = TRUE;
1608 synstate_T *sp;
1609
Bram Moolenaar071d4272004-06-13 20:20:40 +00001610 /*
1611 * Check the state stack when:
1612 * - lnum is just below the previously syntaxed line.
1613 * - lnum is not before the lines with saved states.
1614 * - lnum is not past the lines with saved states.
1615 * - lnum is at or before the last changed line.
1616 */
1617 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1618 {
1619 sp = syn_stack_find_entry(lnum);
1620 if (sp != NULL && sp->sst_lnum == lnum)
1621 {
1622 /*
1623 * finish the previous line (needed when not all of the line was
1624 * drawn)
1625 */
1626 (void)syn_finish_line(FALSE);
1627
1628 /*
1629 * Compare the current state with the previously saved state of
1630 * the line.
1631 */
1632 if (syn_stack_equal(sp))
1633 retval = FALSE;
1634
1635 /*
1636 * Store the current state in b_sst_array[] for later use.
1637 */
1638 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001639 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001640 }
1641 }
1642
Bram Moolenaar071d4272004-06-13 20:20:40 +00001643 return retval;
1644}
1645
1646/*
1647 * Finish the current line.
1648 * This doesn't return any attributes, it only gets the state at the end of
1649 * the line. It can start anywhere in the line, as long as the current state
1650 * is valid.
1651 */
1652 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001653syn_finish_line(
1654 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001655{
1656 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001657 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001658
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001659 while (!current_finished)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001660 {
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001661 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1662 /*
1663 * When syncing, and found some item, need to check the item.
1664 */
1665 if (syncing && current_state.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001666 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001667 /*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001668 * Check for match with sync item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001669 */
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001670 cur_si = &CUR_STATE(current_state.ga_len - 1);
1671 if (cur_si->si_idx >= 0
1672 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1673 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1674 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001675
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001676 /* syn_current_attr() will have skipped the check for an item
1677 * that ends here, need to do that now. Be careful not to go
1678 * past the NUL. */
1679 prev_current_col = current_col;
1680 if (syn_getcurline()[current_col] != NUL)
1681 ++current_col;
1682 check_state_ends();
1683 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001684 }
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001685 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001686 }
1687 return FALSE;
1688}
1689
1690/*
1691 * Return highlight attributes for next character.
1692 * Must first call syntax_start() once for the line.
1693 * "col" is normally 0 for the first use in a line, and increments by one each
1694 * time. It's allowed to skip characters and to stop before the end of the
1695 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001696 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1697 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001698 */
1699 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001700get_syntax_attr(
1701 colnr_T col,
1702 int *can_spell,
1703 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001704{
1705 int attr = 0;
1706
Bram Moolenaar349955a2007-08-14 21:07:36 +00001707 if (can_spell != NULL)
1708 /* Default: Only do spelling when there is no @Spell cluster or when
1709 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001710 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1711 ? (syn_block->b_spell_cluster_id == 0)
1712 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001713
Bram Moolenaar071d4272004-06-13 20:20:40 +00001714 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001715 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001716 return 0;
1717
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001718 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001719 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001720 {
1721 clear_current_state();
1722#ifdef FEAT_EVAL
1723 current_id = 0;
1724 current_trans_id = 0;
1725#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001726#ifdef FEAT_CONCEAL
1727 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001728 current_seqnr = 0;
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001729#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001730 return 0;
1731 }
1732
Bram Moolenaar071d4272004-06-13 20:20:40 +00001733 /* Make sure current_state is valid */
1734 if (INVALID_STATE(&current_state))
1735 validate_current_state();
1736
1737 /*
1738 * Skip from the current column to "col", get the attributes for "col".
1739 */
1740 while (current_col <= col)
1741 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001742 attr = syn_current_attr(FALSE, TRUE, can_spell,
1743 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001744 ++current_col;
1745 }
1746
Bram Moolenaar071d4272004-06-13 20:20:40 +00001747 return attr;
1748}
1749
1750/*
1751 * Get syntax attributes for current_lnum, current_col.
1752 */
1753 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001754syn_current_attr(
1755 int syncing, /* When 1: called for syncing */
1756 int displaying, /* result will be displayed */
1757 int *can_spell, /* return: do spell checking */
1758 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001759{
1760 int syn_id;
1761 lpos_T endpos; /* was: char_u *endp; */
1762 lpos_T hl_startpos; /* was: int hl_startcol; */
1763 lpos_T hl_endpos;
1764 lpos_T eos_pos; /* end-of-start match (start region) */
1765 lpos_T eoe_pos; /* end-of-end pattern */
1766 int end_idx; /* group ID for end pattern */
1767 int idx;
1768 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001769 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001770 int startcol;
1771 int endcol;
1772 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001773 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001774 short *next_list;
1775 int found_match; /* found usable match */
1776 static int try_next_column = FALSE; /* must try in next col */
1777 int do_keywords;
1778 regmmatch_T regmatch;
1779 lpos_T pos;
1780 int lc_col;
1781 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001782 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001783 char_u *line; /* current line. NOTE: becomes invalid after
1784 looking for a pattern match! */
1785
1786 /* variables for zero-width matches that have a "nextgroup" argument */
1787 int keep_next_list;
1788 int zero_width_next_list = FALSE;
1789 garray_T zero_width_next_ga;
1790
1791 /*
1792 * No character, no attributes! Past end of line?
1793 * Do try matching with an empty line (could be the start of a region).
1794 */
1795 line = syn_getcurline();
1796 if (line[current_col] == NUL && current_col != 0)
1797 {
1798 /*
1799 * If we found a match after the last column, use it.
1800 */
1801 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1802 && next_match_col != MAXCOL)
1803 (void)push_next_match(NULL);
1804
1805 current_finished = TRUE;
1806 current_state_stored = FALSE;
1807 return 0;
1808 }
1809
1810 /* if the current or next character is NUL, we will finish the line now */
1811 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1812 {
1813 current_finished = TRUE;
1814 current_state_stored = FALSE;
1815 }
1816
1817 /*
1818 * When in the previous column there was a match but it could not be used
1819 * (empty match or already matched in this column) need to try again in
1820 * the next column.
1821 */
1822 if (try_next_column)
1823 {
1824 next_match_idx = -1;
1825 try_next_column = FALSE;
1826 }
1827
1828 /* Only check for keywords when not syncing and there are some. */
1829 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001830 && (syn_block->b_keywtab.ht_used > 0
1831 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001832
1833 /* Init the list of zero-width matches with a nextlist. This is used to
1834 * avoid matching the same item in the same position twice. */
1835 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1836
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001837 /* use syntax iskeyword option */
1838 save_chartab(buf_chartab);
1839
Bram Moolenaar071d4272004-06-13 20:20:40 +00001840 /*
1841 * Repeat matching keywords and patterns, to find contained items at the
1842 * same column. This stops when there are no extra matches at the current
1843 * column.
1844 */
1845 do
1846 {
1847 found_match = FALSE;
1848 keep_next_list = FALSE;
1849 syn_id = 0;
1850
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001851
Bram Moolenaar071d4272004-06-13 20:20:40 +00001852 /*
1853 * 1. Check for a current state.
1854 * Only when there is no current state, or if the current state may
1855 * contain other things, we need to check for keywords and patterns.
1856 * Always need to check for contained items if some item has the
1857 * "containedin" argument (takes extra time!).
1858 */
1859 if (current_state.ga_len)
1860 cur_si = &CUR_STATE(current_state.ga_len - 1);
1861 else
1862 cur_si = NULL;
1863
Bram Moolenaar860cae12010-06-05 23:22:07 +02001864 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001865 || cur_si->si_cont_list != NULL)
1866 {
1867 /*
1868 * 2. Check for keywords, if on a keyword char after a non-keyword
1869 * char. Don't do this when syncing.
1870 */
1871 if (do_keywords)
1872 {
1873 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001874 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001875 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001876 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00001877 - (has_mbyte
1878 ? (*mb_head_off)(line, line + current_col - 1)
Bram Moolenaar264b74f2019-01-24 17:18:42 +01001879 : 0) , syn_buf)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001880 {
1881 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001882 &endcol, &flags, &next_list, cur_si,
1883 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001884 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001885 {
1886 if (push_current_state(KEYWORD_IDX) == OK)
1887 {
1888 cur_si = &CUR_STATE(current_state.ga_len - 1);
1889 cur_si->si_m_startcol = current_col;
1890 cur_si->si_h_startpos.lnum = current_lnum;
1891 cur_si->si_h_startpos.col = 0; /* starts right away */
1892 cur_si->si_m_endpos.lnum = current_lnum;
1893 cur_si->si_m_endpos.col = endcol;
1894 cur_si->si_h_endpos.lnum = current_lnum;
1895 cur_si->si_h_endpos.col = endcol;
1896 cur_si->si_ends = TRUE;
1897 cur_si->si_end_idx = 0;
1898 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001899#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02001900 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001901 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001902 if (current_state.ga_len > 1)
1903 cur_si->si_flags |=
1904 CUR_STATE(current_state.ga_len - 2).si_flags
1905 & HL_CONCEAL;
1906#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001907 cur_si->si_id = syn_id;
1908 cur_si->si_trans_id = syn_id;
1909 if (flags & HL_TRANSP)
1910 {
1911 if (current_state.ga_len < 2)
1912 {
1913 cur_si->si_attr = 0;
1914 cur_si->si_trans_id = 0;
1915 }
1916 else
1917 {
1918 cur_si->si_attr = CUR_STATE(
1919 current_state.ga_len - 2).si_attr;
1920 cur_si->si_trans_id = CUR_STATE(
1921 current_state.ga_len - 2).si_trans_id;
1922 }
1923 }
1924 else
1925 cur_si->si_attr = syn_id2attr(syn_id);
1926 cur_si->si_cont_list = NULL;
1927 cur_si->si_next_list = next_list;
1928 check_keepend();
1929 }
1930 else
1931 vim_free(next_list);
1932 }
1933 }
1934 }
1935
1936 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001937 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001938 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001939 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001940 {
1941 /*
1942 * If we didn't check for a match yet, or we are past it, check
1943 * for any match with a pattern.
1944 */
1945 if (next_match_idx < 0 || next_match_col < (int)current_col)
1946 {
1947 /*
1948 * Check all relevant patterns for a match at this
1949 * position. This is complicated, because matching with a
1950 * pattern takes quite a bit of time, thus we want to
1951 * avoid doing it when it's not needed.
1952 */
1953 next_match_idx = 0; /* no match in this line yet */
1954 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001955 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001956 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001957 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001958 if ( spp->sp_syncing == syncing
1959 && (displaying || !(spp->sp_flags & HL_DISPLAY))
1960 && (spp->sp_type == SPTYPE_MATCH
1961 || spp->sp_type == SPTYPE_START)
1962 && (current_next_list != NULL
1963 ? in_id_list(NULL, current_next_list,
1964 &spp->sp_syn, 0)
1965 : (cur_si == NULL
1966 ? !(spp->sp_flags & HL_CONTAINED)
1967 : in_id_list(cur_si,
1968 cur_si->si_cont_list, &spp->sp_syn,
1969 spp->sp_flags & HL_CONTAINED))))
1970 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001971 int r;
1972
Bram Moolenaar071d4272004-06-13 20:20:40 +00001973 /* If we already tried matching in this line, and
1974 * there isn't a match before next_match_col, skip
1975 * this item. */
1976 if (spp->sp_line_id == current_line_id
1977 && spp->sp_startcol >= next_match_col)
1978 continue;
1979 spp->sp_line_id = current_line_id;
1980
1981 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
1982 if (lc_col < 0)
1983 lc_col = 0;
1984
1985 regmatch.rmm_ic = spp->sp_ic;
1986 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001987 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001988 current_lnum,
1989 (colnr_T)lc_col,
Bram Moolenaard23a8232018-02-10 18:45:26 +01001990 IF_SYN_TIME(&spp->sp_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001991 spp->sp_prog = regmatch.regprog;
1992 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001993 {
1994 /* no match in this line, try another one */
1995 spp->sp_startcol = MAXCOL;
1996 continue;
1997 }
1998
1999 /*
2000 * Compute the first column of the match.
2001 */
2002 syn_add_start_off(&pos, &regmatch,
2003 spp, SPO_MS_OFF, -1);
2004 if (pos.lnum > current_lnum)
2005 {
2006 /* must have used end of match in a next line,
2007 * we can't handle that */
2008 spp->sp_startcol = MAXCOL;
2009 continue;
2010 }
2011 startcol = pos.col;
2012
2013 /* remember the next column where this pattern
2014 * matches in the current line */
2015 spp->sp_startcol = startcol;
2016
2017 /*
2018 * If a previously found match starts at a lower
2019 * column number, don't use this one.
2020 */
2021 if (startcol >= next_match_col)
2022 continue;
2023
2024 /*
2025 * If we matched this pattern at this position
2026 * before, skip it. Must retry in the next
2027 * column, because it may match from there.
2028 */
2029 if (did_match_already(idx, &zero_width_next_ga))
2030 {
2031 try_next_column = TRUE;
2032 continue;
2033 }
2034
2035 endpos.lnum = regmatch.endpos[0].lnum;
2036 endpos.col = regmatch.endpos[0].col;
2037
2038 /* Compute the highlight start. */
2039 syn_add_start_off(&hl_startpos, &regmatch,
2040 spp, SPO_HS_OFF, -1);
2041
2042 /* Compute the region start. */
2043 /* Default is to use the end of the match. */
2044 syn_add_end_off(&eos_pos, &regmatch,
2045 spp, SPO_RS_OFF, 0);
2046
2047 /*
2048 * Grab the external submatches before they get
2049 * overwritten. Reference count doesn't change.
2050 */
2051 unref_extmatch(cur_extmatch);
2052 cur_extmatch = re_extmatch_out;
2053 re_extmatch_out = NULL;
2054
2055 flags = 0;
2056 eoe_pos.lnum = 0; /* avoid warning */
2057 eoe_pos.col = 0;
2058 end_idx = 0;
2059 hl_endpos.lnum = 0;
2060
2061 /*
2062 * For a "oneline" the end must be found in the
2063 * same line too. Search for it after the end of
2064 * the match with the start pattern. Set the
2065 * resulting end positions at the same time.
2066 */
2067 if (spp->sp_type == SPTYPE_START
2068 && (spp->sp_flags & HL_ONELINE))
2069 {
2070 lpos_T startpos;
2071
2072 startpos = endpos;
2073 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2074 &flags, &eoe_pos, &end_idx, cur_extmatch);
2075 if (endpos.lnum == 0)
2076 continue; /* not found */
2077 }
2078
2079 /*
2080 * For a "match" the size must be > 0 after the
2081 * end offset needs has been added. Except when
2082 * syncing.
2083 */
2084 else if (spp->sp_type == SPTYPE_MATCH)
2085 {
2086 syn_add_end_off(&hl_endpos, &regmatch, spp,
2087 SPO_HE_OFF, 0);
2088 syn_add_end_off(&endpos, &regmatch, spp,
2089 SPO_ME_OFF, 0);
2090 if (endpos.lnum == current_lnum
2091 && (int)endpos.col + syncing < startcol)
2092 {
2093 /*
2094 * If an empty string is matched, may need
2095 * to try matching again at next column.
2096 */
2097 if (regmatch.startpos[0].col
2098 == regmatch.endpos[0].col)
2099 try_next_column = TRUE;
2100 continue;
2101 }
2102 }
2103
2104 /*
2105 * keep the best match so far in next_match_*
2106 */
2107 /* Highlighting must start after startpos and end
2108 * before endpos. */
2109 if (hl_startpos.lnum == current_lnum
2110 && (int)hl_startpos.col < startcol)
2111 hl_startpos.col = startcol;
2112 limit_pos_zero(&hl_endpos, &endpos);
2113
2114 next_match_idx = idx;
2115 next_match_col = startcol;
2116 next_match_m_endpos = endpos;
2117 next_match_h_endpos = hl_endpos;
2118 next_match_h_startpos = hl_startpos;
2119 next_match_flags = flags;
2120 next_match_eos_pos = eos_pos;
2121 next_match_eoe_pos = eoe_pos;
2122 next_match_end_idx = end_idx;
2123 unref_extmatch(next_match_extmatch);
2124 next_match_extmatch = cur_extmatch;
2125 cur_extmatch = NULL;
2126 }
2127 }
2128 }
2129
2130 /*
2131 * If we found a match at the current column, use it.
2132 */
2133 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2134 {
2135 synpat_T *lspp;
2136
2137 /* When a zero-width item matched which has a nextgroup,
2138 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002139 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002140 if (next_match_m_endpos.lnum == current_lnum
2141 && next_match_m_endpos.col == current_col
2142 && lspp->sp_next_list != NULL)
2143 {
2144 current_next_list = lspp->sp_next_list;
2145 current_next_flags = lspp->sp_flags;
2146 keep_next_list = TRUE;
2147 zero_width_next_list = TRUE;
2148
2149 /* Add the index to a list, so that we can check
2150 * later that we don't match it again (and cause an
2151 * endless loop). */
2152 if (ga_grow(&zero_width_next_ga, 1) == OK)
2153 {
2154 ((int *)(zero_width_next_ga.ga_data))
2155 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002156 }
2157 next_match_idx = -1;
2158 }
2159 else
2160 cur_si = push_next_match(cur_si);
2161 found_match = TRUE;
2162 }
2163 }
2164 }
2165
2166 /*
2167 * Handle searching for nextgroup match.
2168 */
2169 if (current_next_list != NULL && !keep_next_list)
2170 {
2171 /*
2172 * If a nextgroup was not found, continue looking for one if:
2173 * - this is an empty line and the "skipempty" option was given
2174 * - we are on white space and the "skipwhite" option was given
2175 */
2176 if (!found_match)
2177 {
2178 line = syn_getcurline();
2179 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002180 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002181 || ((current_next_flags & HL_SKIPEMPTY)
2182 && *line == NUL))
2183 break;
2184 }
2185
2186 /*
2187 * If a nextgroup was found: Use it, and continue looking for
2188 * contained matches.
2189 * If a nextgroup was not found: Continue looking for a normal
2190 * match.
2191 * When did set current_next_list for a zero-width item and no
2192 * match was found don't loop (would get stuck).
2193 */
2194 current_next_list = NULL;
2195 next_match_idx = -1;
2196 if (!zero_width_next_list)
2197 found_match = TRUE;
2198 }
2199
2200 } while (found_match);
2201
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002202 restore_chartab(buf_chartab);
2203
Bram Moolenaar071d4272004-06-13 20:20:40 +00002204 /*
2205 * Use attributes from the current state, if within its highlighting.
2206 * If not, use attributes from the current-but-one state, etc.
2207 */
2208 current_attr = 0;
2209#ifdef FEAT_EVAL
2210 current_id = 0;
2211 current_trans_id = 0;
2212#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002213#ifdef FEAT_CONCEAL
2214 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02002215 current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002216#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002217 if (cur_si != NULL)
2218 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002219#ifndef FEAT_EVAL
2220 int current_trans_id = 0;
2221#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002222 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2223 {
2224 sip = &CUR_STATE(idx);
2225 if ((current_lnum > sip->si_h_startpos.lnum
2226 || (current_lnum == sip->si_h_startpos.lnum
2227 && current_col >= sip->si_h_startpos.col))
2228 && (sip->si_h_endpos.lnum == 0
2229 || current_lnum < sip->si_h_endpos.lnum
2230 || (current_lnum == sip->si_h_endpos.lnum
2231 && current_col < sip->si_h_endpos.col)))
2232 {
2233 current_attr = sip->si_attr;
2234#ifdef FEAT_EVAL
2235 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002236#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002237 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002238#ifdef FEAT_CONCEAL
2239 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002240 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002241 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002242#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002243 break;
2244 }
2245 }
2246
Bram Moolenaar217ad922005-03-20 22:37:15 +00002247 if (can_spell != NULL)
2248 {
2249 struct sp_syn sps;
2250
2251 /*
2252 * set "can_spell" to TRUE if spell checking is supposed to be
2253 * done in the current item.
2254 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002255 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002256 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002257 /* There is no @Spell cluster: Do spelling for items without
2258 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002259 if (syn_block->b_nospell_cluster_id == 0
2260 || current_trans_id == 0)
2261 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002262 else
2263 {
2264 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002265 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002266 sps.cont_in_list = NULL;
2267 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2268 }
2269 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002270 else
2271 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002272 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002273 * the @Spell cluster. But not when @NoSpell is also there.
2274 * At the toplevel only spell check when ":syn spell toplevel"
2275 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002276 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002277 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002278 else
2279 {
2280 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002281 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002282 sps.cont_in_list = NULL;
2283 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2284
Bram Moolenaar860cae12010-06-05 23:22:07 +02002285 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002286 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002287 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002288 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2289 *can_spell = FALSE;
2290 }
2291 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002292 }
2293 }
2294
2295
Bram Moolenaar071d4272004-06-13 20:20:40 +00002296 /*
2297 * Check for end of current state (and the states before it) at the
2298 * next column. Don't do this for syncing, because we would miss a
2299 * single character match.
2300 * First check if the current state ends at the current column. It
2301 * may be for an empty match and a containing item might end in the
2302 * current column.
2303 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002304 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002305 {
2306 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002307 if (current_state.ga_len > 0
2308 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002309 {
2310 ++current_col;
2311 check_state_ends();
2312 --current_col;
2313 }
2314 }
2315 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002316 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002317 /* Default: Only do spelling when there is no @Spell cluster or when
2318 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002319 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2320 ? (syn_block->b_spell_cluster_id == 0)
2321 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002322
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002323 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002324 if (current_next_list != NULL
Bram Moolenaar069dafc2018-03-03 20:02:19 +01002325 && (line = syn_getcurline())[current_col] != NUL
2326 && line[current_col + 1] == NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002327 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2328 current_next_list = NULL;
2329
2330 if (zero_width_next_ga.ga_len > 0)
2331 ga_clear(&zero_width_next_ga);
2332
2333 /* No longer need external matches. But keep next_match_extmatch. */
2334 unref_extmatch(re_extmatch_out);
2335 re_extmatch_out = NULL;
2336 unref_extmatch(cur_extmatch);
2337
2338 return current_attr;
2339}
2340
2341
2342/*
2343 * Check if we already matched pattern "idx" at the current column.
2344 */
2345 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002346did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002347{
2348 int i;
2349
2350 for (i = current_state.ga_len; --i >= 0; )
2351 if (CUR_STATE(i).si_m_startcol == (int)current_col
2352 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2353 && CUR_STATE(i).si_idx == idx)
2354 return TRUE;
2355
2356 /* Zero-width matches with a nextgroup argument are not put on the syntax
2357 * stack, and can only be matched once anyway. */
2358 for (i = gap->ga_len; --i >= 0; )
2359 if (((int *)(gap->ga_data))[i] == idx)
2360 return TRUE;
2361
2362 return FALSE;
2363}
2364
2365/*
2366 * Push the next match onto the stack.
2367 */
2368 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002369push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002370{
2371 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002372#ifdef FEAT_CONCEAL
2373 int save_flags;
2374#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002375
Bram Moolenaar860cae12010-06-05 23:22:07 +02002376 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002377
2378 /*
2379 * Push the item in current_state stack;
2380 */
2381 if (push_current_state(next_match_idx) == OK)
2382 {
2383 /*
2384 * If it's a start-skip-end type that crosses lines, figure out how
2385 * much it continues in this line. Otherwise just fill in the length.
2386 */
2387 cur_si = &CUR_STATE(current_state.ga_len - 1);
2388 cur_si->si_h_startpos = next_match_h_startpos;
2389 cur_si->si_m_startcol = current_col;
2390 cur_si->si_m_lnum = current_lnum;
2391 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002392#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002393 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002394 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002395 if (current_state.ga_len > 1)
2396 cur_si->si_flags |=
2397 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2398#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002399 cur_si->si_next_list = spp->sp_next_list;
2400 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2401 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2402 {
2403 /* Try to find the end pattern in the current line */
2404 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2405 check_keepend();
2406 }
2407 else
2408 {
2409 cur_si->si_m_endpos = next_match_m_endpos;
2410 cur_si->si_h_endpos = next_match_h_endpos;
2411 cur_si->si_ends = TRUE;
2412 cur_si->si_flags |= next_match_flags;
2413 cur_si->si_eoe_pos = next_match_eoe_pos;
2414 cur_si->si_end_idx = next_match_end_idx;
2415 }
2416 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2417 keepend_level = current_state.ga_len - 1;
2418 check_keepend();
2419 update_si_attr(current_state.ga_len - 1);
2420
Bram Moolenaar860cae12010-06-05 23:22:07 +02002421#ifdef FEAT_CONCEAL
2422 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2423#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002424 /*
2425 * If the start pattern has another highlight group, push another item
2426 * on the stack for the start pattern.
2427 */
2428 if ( spp->sp_type == SPTYPE_START
2429 && spp->sp_syn_match_id != 0
2430 && push_current_state(next_match_idx) == OK)
2431 {
2432 cur_si = &CUR_STATE(current_state.ga_len - 1);
2433 cur_si->si_h_startpos = next_match_h_startpos;
2434 cur_si->si_m_startcol = current_col;
2435 cur_si->si_m_lnum = current_lnum;
2436 cur_si->si_m_endpos = next_match_eos_pos;
2437 cur_si->si_h_endpos = next_match_eos_pos;
2438 cur_si->si_ends = TRUE;
2439 cur_si->si_end_idx = 0;
2440 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002441#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002442 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002443 cur_si->si_flags |= save_flags;
2444 if (cur_si->si_flags & HL_CONCEALENDS)
2445 cur_si->si_flags |= HL_CONCEAL;
2446#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002447 cur_si->si_next_list = NULL;
2448 check_keepend();
2449 update_si_attr(current_state.ga_len - 1);
2450 }
2451 }
2452
2453 next_match_idx = -1; /* try other match next time */
2454
2455 return cur_si;
2456}
2457
2458/*
2459 * Check for end of current state (and the states before it).
2460 */
2461 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002462check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002463{
2464 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002465 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002466
2467 cur_si = &CUR_STATE(current_state.ga_len - 1);
2468 for (;;)
2469 {
2470 if (cur_si->si_ends
2471 && (cur_si->si_m_endpos.lnum < current_lnum
2472 || (cur_si->si_m_endpos.lnum == current_lnum
2473 && cur_si->si_m_endpos.col <= current_col)))
2474 {
2475 /*
2476 * If there is an end pattern group ID, highlight the end pattern
2477 * now. No need to pop the current item from the stack.
2478 * Only do this if the end pattern continues beyond the current
2479 * position.
2480 */
2481 if (cur_si->si_end_idx
2482 && (cur_si->si_eoe_pos.lnum > current_lnum
2483 || (cur_si->si_eoe_pos.lnum == current_lnum
2484 && cur_si->si_eoe_pos.col > current_col)))
2485 {
2486 cur_si->si_idx = cur_si->si_end_idx;
2487 cur_si->si_end_idx = 0;
2488 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2489 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2490 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002491#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002492 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002493 if (cur_si->si_flags & HL_CONCEALENDS)
2494 cur_si->si_flags |= HL_CONCEAL;
2495#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002496 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002497
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002498 /* nextgroup= should not match in the end pattern */
2499 current_next_list = NULL;
2500
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002501 /* what matches next may be different now, clear it */
2502 next_match_idx = 0;
2503 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002504 break;
2505 }
2506 else
2507 {
2508 /* handle next_list, unless at end of line and no "skipnl" or
2509 * "skipempty" */
2510 current_next_list = cur_si->si_next_list;
2511 current_next_flags = cur_si->si_flags;
2512 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2513 && syn_getcurline()[current_col] == NUL)
2514 current_next_list = NULL;
2515
2516 /* When the ended item has "extend", another item with
2517 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002518 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002519
2520 pop_current_state();
2521
2522 if (current_state.ga_len == 0)
2523 break;
2524
Bram Moolenaar81993f42008-01-11 20:27:45 +00002525 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002526 {
2527 syn_update_ends(FALSE);
2528 if (current_state.ga_len == 0)
2529 break;
2530 }
2531
2532 cur_si = &CUR_STATE(current_state.ga_len - 1);
2533
2534 /*
2535 * Only for a region the search for the end continues after
2536 * the end of the contained item. If the contained match
2537 * included the end-of-line, break here, the region continues.
2538 * Don't do this when:
2539 * - "keepend" is used for the contained item
2540 * - not at the end of the line (could be end="x$"me=e-1).
2541 * - "excludenl" is used (HL_HAS_EOL won't be set)
2542 */
2543 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002544 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002545 == SPTYPE_START
2546 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2547 {
2548 update_si_end(cur_si, (int)current_col, TRUE);
2549 check_keepend();
2550 if ((current_next_flags & HL_HAS_EOL)
2551 && keepend_level < 0
2552 && syn_getcurline()[current_col] == NUL)
2553 break;
2554 }
2555 }
2556 }
2557 else
2558 break;
2559 }
2560}
2561
2562/*
2563 * Update an entry in the current_state stack for a match or region. This
2564 * fills in si_attr, si_next_list and si_cont_list.
2565 */
2566 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002567update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002568{
2569 stateitem_T *sip = &CUR_STATE(idx);
2570 synpat_T *spp;
2571
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002572 /* This should not happen... */
2573 if (sip->si_idx < 0)
2574 return;
2575
Bram Moolenaar860cae12010-06-05 23:22:07 +02002576 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002577 if (sip->si_flags & HL_MATCH)
2578 sip->si_id = spp->sp_syn_match_id;
2579 else
2580 sip->si_id = spp->sp_syn.id;
2581 sip->si_attr = syn_id2attr(sip->si_id);
2582 sip->si_trans_id = sip->si_id;
2583 if (sip->si_flags & HL_MATCH)
2584 sip->si_cont_list = NULL;
2585 else
2586 sip->si_cont_list = spp->sp_cont_list;
2587
2588 /*
2589 * For transparent items, take attr from outer item.
2590 * Also take cont_list, if there is none.
2591 * Don't do this for the matchgroup of a start or end pattern.
2592 */
2593 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2594 {
2595 if (idx == 0)
2596 {
2597 sip->si_attr = 0;
2598 sip->si_trans_id = 0;
2599 if (sip->si_cont_list == NULL)
2600 sip->si_cont_list = ID_LIST_ALL;
2601 }
2602 else
2603 {
2604 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2605 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002606 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2607 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002608 if (sip->si_cont_list == NULL)
2609 {
2610 sip->si_flags |= HL_TRANS_CONT;
2611 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2612 }
2613 }
2614 }
2615}
2616
2617/*
2618 * Check the current stack for patterns with "keepend" flag.
2619 * Propagate the match-end to contained items, until a "skipend" item is found.
2620 */
2621 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002622check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002623{
2624 int i;
2625 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002626 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002627 stateitem_T *sip;
2628
2629 /*
2630 * This check can consume a lot of time; only do it from the level where
2631 * there really is a keepend.
2632 */
2633 if (keepend_level < 0)
2634 return;
2635
2636 /*
2637 * Find the last index of an "extend" item. "keepend" items before that
2638 * won't do anything. If there is no "extend" item "i" will be
2639 * "keepend_level" and all "keepend" items will work normally.
2640 */
2641 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2642 if (CUR_STATE(i).si_flags & HL_EXTEND)
2643 break;
2644
2645 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002646 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002647 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002648 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002649 for ( ; i < current_state.ga_len; ++i)
2650 {
2651 sip = &CUR_STATE(i);
2652 if (maxpos.lnum != 0)
2653 {
2654 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002655 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002656 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2657 sip->si_ends = TRUE;
2658 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002659 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2660 {
2661 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002662 || maxpos.lnum > sip->si_m_endpos.lnum
2663 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002664 && maxpos.col > sip->si_m_endpos.col))
2665 maxpos = sip->si_m_endpos;
2666 if (maxpos_h.lnum == 0
2667 || maxpos_h.lnum > sip->si_h_endpos.lnum
2668 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2669 && maxpos_h.col > sip->si_h_endpos.col))
2670 maxpos_h = sip->si_h_endpos;
2671 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002672 }
2673}
2674
2675/*
2676 * Update an entry in the current_state stack for a start-skip-end pattern.
2677 * This finds the end of the current item, if it's in the current line.
2678 *
2679 * Return the flags for the matched END.
2680 */
2681 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002682update_si_end(
2683 stateitem_T *sip,
2684 int startcol, /* where to start searching for the end */
2685 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002686{
2687 lpos_T startpos;
2688 lpos_T endpos;
2689 lpos_T hl_endpos;
2690 lpos_T end_endpos;
2691 int end_idx;
2692
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002693 /* return quickly for a keyword */
2694 if (sip->si_idx < 0)
2695 return;
2696
Bram Moolenaar071d4272004-06-13 20:20:40 +00002697 /* Don't update when it's already done. Can be a match of an end pattern
2698 * that started in a previous line. Watch out: can also be a "keepend"
2699 * from a containing item. */
2700 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2701 return;
2702
2703 /*
2704 * We need to find the end of the region. It may continue in the next
2705 * line.
2706 */
2707 end_idx = 0;
2708 startpos.lnum = current_lnum;
2709 startpos.col = startcol;
2710 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2711 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2712
2713 if (endpos.lnum == 0)
2714 {
2715 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002716 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002717 {
2718 /* a "oneline" never continues in the next line */
2719 sip->si_ends = TRUE;
2720 sip->si_m_endpos.lnum = current_lnum;
2721 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2722 }
2723 else
2724 {
2725 /* continues in the next line */
2726 sip->si_ends = FALSE;
2727 sip->si_m_endpos.lnum = 0;
2728 }
2729 sip->si_h_endpos = sip->si_m_endpos;
2730 }
2731 else
2732 {
2733 /* match within this line */
2734 sip->si_m_endpos = endpos;
2735 sip->si_h_endpos = hl_endpos;
2736 sip->si_eoe_pos = end_endpos;
2737 sip->si_ends = TRUE;
2738 sip->si_end_idx = end_idx;
2739 }
2740}
2741
2742/*
2743 * Add a new state to the current state stack.
2744 * It is cleared and the index set to "idx".
2745 * Return FAIL if it's not possible (out of memory).
2746 */
2747 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002748push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002749{
2750 if (ga_grow(&current_state, 1) == FAIL)
2751 return FAIL;
2752 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2753 CUR_STATE(current_state.ga_len).si_idx = idx;
2754 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002755 return OK;
2756}
2757
2758/*
2759 * Remove a state from the current_state stack.
2760 */
2761 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002762pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002763{
2764 if (current_state.ga_len)
2765 {
2766 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2767 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002768 }
2769 /* after the end of a pattern, try matching a keyword or pattern */
2770 next_match_idx = -1;
2771
2772 /* if first state with "keepend" is popped, reset keepend_level */
2773 if (keepend_level >= current_state.ga_len)
2774 keepend_level = -1;
2775}
2776
2777/*
2778 * Find the end of a start/skip/end syntax region after "startpos".
2779 * Only checks one line.
2780 * Also handles a match item that continued from a previous line.
2781 * If not found, the syntax item continues in the next line. m_endpos->lnum
2782 * will be 0.
2783 * If found, the end of the region and the end of the highlighting is
2784 * computed.
2785 */
2786 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002787find_endpos(
2788 int idx, /* index of the pattern */
2789 lpos_T *startpos, /* where to start looking for an END match */
2790 lpos_T *m_endpos, /* return: end of match */
2791 lpos_T *hl_endpos, /* return: end of highlighting */
2792 long *flagsp, /* return: flags of matching END */
2793 lpos_T *end_endpos, /* return: end of end pattern match */
2794 int *end_idx, /* return: group ID for end pat. match, or 0 */
2795 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002796{
2797 colnr_T matchcol;
2798 synpat_T *spp, *spp_skip;
2799 int start_idx;
2800 int best_idx;
2801 regmmatch_T regmatch;
2802 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2803 lpos_T pos;
2804 char_u *line;
2805 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002806 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002807
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002808 /* just in case we are invoked for a keyword */
2809 if (idx < 0)
2810 return;
2811
Bram Moolenaar071d4272004-06-13 20:20:40 +00002812 /*
2813 * Check for being called with a START pattern.
2814 * Can happen with a match that continues to the next line, because it
2815 * contained a region.
2816 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002817 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002818 if (spp->sp_type != SPTYPE_START)
2819 {
2820 *hl_endpos = *startpos;
2821 return;
2822 }
2823
2824 /*
2825 * Find the SKIP or first END pattern after the last START pattern.
2826 */
2827 for (;;)
2828 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002829 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002830 if (spp->sp_type != SPTYPE_START)
2831 break;
2832 ++idx;
2833 }
2834
2835 /*
2836 * Lookup the SKIP pattern (if present)
2837 */
2838 if (spp->sp_type == SPTYPE_SKIP)
2839 {
2840 spp_skip = spp;
2841 ++idx;
2842 }
2843 else
2844 spp_skip = NULL;
2845
2846 /* Setup external matches for syn_regexec(). */
2847 unref_extmatch(re_extmatch_in);
2848 re_extmatch_in = ref_extmatch(start_ext);
2849
2850 matchcol = startpos->col; /* start looking for a match at sstart */
2851 start_idx = idx; /* remember the first END pattern. */
2852 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002853
2854 /* use syntax iskeyword option */
2855 save_chartab(buf_chartab);
2856
Bram Moolenaar071d4272004-06-13 20:20:40 +00002857 for (;;)
2858 {
2859 /*
2860 * Find end pattern that matches first after "matchcol".
2861 */
2862 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002863 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002864 {
2865 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002866 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002867
Bram Moolenaar860cae12010-06-05 23:22:07 +02002868 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002869 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2870 break;
2871 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2872 if (lc_col < 0)
2873 lc_col = 0;
2874
2875 regmatch.rmm_ic = spp->sp_ic;
2876 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002877 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
2878 IF_SYN_TIME(&spp->sp_time));
2879 spp->sp_prog = regmatch.regprog;
2880 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002881 {
2882 if (best_idx == -1 || regmatch.startpos[0].col
2883 < best_regmatch.startpos[0].col)
2884 {
2885 best_idx = idx;
2886 best_regmatch.startpos[0] = regmatch.startpos[0];
2887 best_regmatch.endpos[0] = regmatch.endpos[0];
2888 }
2889 }
2890 }
2891
2892 /*
2893 * If all end patterns have been tried, and there is no match, the
2894 * item continues until end-of-line.
2895 */
2896 if (best_idx == -1)
2897 break;
2898
2899 /*
2900 * If the skip pattern matches before the end pattern,
2901 * continue searching after the skip pattern.
2902 */
2903 if (spp_skip != NULL)
2904 {
2905 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002906 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002907
2908 if (lc_col < 0)
2909 lc_col = 0;
2910 regmatch.rmm_ic = spp_skip->sp_ic;
2911 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002912 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
2913 IF_SYN_TIME(&spp_skip->sp_time));
2914 spp_skip->sp_prog = regmatch.regprog;
2915 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00002916 <= best_regmatch.startpos[0].col)
2917 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01002918 int line_len;
2919
Bram Moolenaar071d4272004-06-13 20:20:40 +00002920 /* Add offset to skip pattern match */
2921 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2922
2923 /* If the skip pattern goes on to the next line, there is no
2924 * match with an end pattern in this line. */
2925 if (pos.lnum > startpos->lnum)
2926 break;
2927
2928 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01002929 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002930
2931 /* take care of an empty match or negative offset */
2932 if (pos.col <= matchcol)
2933 ++matchcol;
2934 else if (pos.col <= regmatch.endpos[0].col)
2935 matchcol = pos.col;
2936 else
2937 /* Be careful not to jump over the NUL at the end-of-line */
2938 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01002939 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002940 ++matchcol)
2941 ;
2942
2943 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01002944 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002945 break;
2946
2947 continue; /* start with first end pattern again */
2948 }
2949 }
2950
2951 /*
2952 * Match from start pattern to end pattern.
2953 * Correct for match and highlight offset of end pattern.
2954 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002955 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002956 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
2957 /* can't end before the start */
2958 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
2959 m_endpos->col = startpos->col;
2960
2961 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
2962 /* can't end before the start */
2963 if (end_endpos->lnum == startpos->lnum
2964 && end_endpos->col < startpos->col)
2965 end_endpos->col = startpos->col;
2966 /* can't end after the match */
2967 limit_pos(end_endpos, m_endpos);
2968
2969 /*
2970 * If the end group is highlighted differently, adjust the pointers.
2971 */
2972 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
2973 {
2974 *end_idx = best_idx;
2975 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
2976 {
2977 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
2978 hl_endpos->col = best_regmatch.endpos[0].col;
2979 }
2980 else
2981 {
2982 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
2983 hl_endpos->col = best_regmatch.startpos[0].col;
2984 }
2985 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
2986
2987 /* can't end before the start */
2988 if (hl_endpos->lnum == startpos->lnum
2989 && hl_endpos->col < startpos->col)
2990 hl_endpos->col = startpos->col;
2991 limit_pos(hl_endpos, m_endpos);
2992
2993 /* now the match ends where the highlighting ends, it is turned
2994 * into the matchgroup for the end */
2995 *m_endpos = *hl_endpos;
2996 }
2997 else
2998 {
2999 *end_idx = 0;
3000 *hl_endpos = *end_endpos;
3001 }
3002
3003 *flagsp = spp->sp_flags;
3004
3005 had_match = TRUE;
3006 break;
3007 }
3008
3009 /* no match for an END pattern in this line */
3010 if (!had_match)
3011 m_endpos->lnum = 0;
3012
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003013 restore_chartab(buf_chartab);
3014
Bram Moolenaar071d4272004-06-13 20:20:40 +00003015 /* Remove external matches. */
3016 unref_extmatch(re_extmatch_in);
3017 re_extmatch_in = NULL;
3018}
3019
3020/*
3021 * Limit "pos" not to be after "limit".
3022 */
3023 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003024limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003025{
3026 if (pos->lnum > limit->lnum)
3027 *pos = *limit;
3028 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3029 pos->col = limit->col;
3030}
3031
3032/*
3033 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3034 */
3035 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003036limit_pos_zero(
3037 lpos_T *pos,
3038 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003039{
3040 if (pos->lnum == 0)
3041 *pos = *limit;
3042 else
3043 limit_pos(pos, limit);
3044}
3045
3046/*
3047 * Add offset to matched text for end of match or highlight.
3048 */
3049 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003050syn_add_end_off(
3051 lpos_T *result, /* returned position */
3052 regmmatch_T *regmatch, /* start/end of match */
3053 synpat_T *spp, /* matched pattern */
3054 int idx, /* index of offset */
3055 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003056{
3057 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003058 int off;
3059 char_u *base;
3060 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003061
3062 if (spp->sp_off_flags & (1 << idx))
3063 {
3064 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003065 col = regmatch->startpos[0].col;
3066 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003067 }
3068 else
3069 {
3070 result->lnum = regmatch->endpos[0].lnum;
3071 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003072 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003073 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003074 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3075 * is a matchgroup. Watch out for match with last NL in the buffer. */
3076 if (result->lnum > syn_buf->b_ml.ml_line_count)
3077 col = 0;
3078 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003079 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003080 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3081 p = base + col;
3082 if (off > 0)
3083 {
3084 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003085 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003086 }
3087 else if (off < 0)
3088 {
3089 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003090 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003091 }
3092 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003093 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003094 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003095}
3096
3097/*
3098 * Add offset to matched text for start of match or highlight.
3099 * Avoid resulting column to become negative.
3100 */
3101 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003102syn_add_start_off(
3103 lpos_T *result, /* returned position */
3104 regmmatch_T *regmatch, /* start/end of match */
3105 synpat_T *spp,
3106 int idx,
3107 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003108{
3109 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003110 int off;
3111 char_u *base;
3112 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003113
3114 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3115 {
3116 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003117 col = regmatch->endpos[0].col;
3118 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003119 }
3120 else
3121 {
3122 result->lnum = regmatch->startpos[0].lnum;
3123 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003124 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003125 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003126 if (result->lnum > syn_buf->b_ml.ml_line_count)
3127 {
3128 /* a "\n" at the end of the pattern may take us below the last line */
3129 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003130 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003131 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003132 if (off != 0)
3133 {
3134 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3135 p = base + col;
3136 if (off > 0)
3137 {
3138 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003139 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003140 }
3141 else if (off < 0)
3142 {
3143 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003144 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003145 }
3146 col = (int)(p - base);
3147 }
3148 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003149}
3150
3151/*
3152 * Get current line in syntax buffer.
3153 */
3154 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003155syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003156{
3157 return ml_get_buf(syn_buf, current_lnum, FALSE);
3158}
3159
3160/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003161 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003162 * Returns TRUE when there is a match.
3163 */
3164 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003165syn_regexec(
3166 regmmatch_T *rmp,
3167 linenr_T lnum,
3168 colnr_T col,
3169 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003170{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003171 int r;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003172#ifdef FEAT_RELTIME
3173 int timed_out = FALSE;
3174#endif
Bram Moolenaarf7512552013-06-06 14:55:19 +02003175#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003176 proftime_T pt;
3177
3178 if (syn_time_on)
3179 profile_start(&pt);
3180#endif
3181
Bram Moolenaarbcf94422018-06-23 14:21:42 +02003182 if (rmp->regprog == NULL)
3183 // This can happen if a previous call to vim_regexec_multi() tried to
3184 // use the NFA engine, which resulted in NFA_TOO_EXPENSIVE, and
3185 // compiling the pattern with the other engine fails.
3186 return FALSE;
3187
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003188 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003189 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
3190#ifdef FEAT_RELTIME
3191 syn_tm, &timed_out
3192#else
3193 NULL, NULL
3194#endif
3195 );
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003196
Bram Moolenaarf7512552013-06-06 14:55:19 +02003197#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003198 if (syn_time_on)
3199 {
3200 profile_end(&pt);
3201 profile_add(&st->total, &pt);
3202 if (profile_cmp(&pt, &st->slowest) < 0)
3203 st->slowest = pt;
3204 ++st->count;
3205 if (r > 0)
3206 ++st->match;
3207 }
3208#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003209#ifdef FEAT_RELTIME
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003210 if (timed_out && !syn_win->w_s->b_syn_slow)
3211 {
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003212 syn_win->w_s->b_syn_slow = TRUE;
Bram Moolenaar32526b32019-01-19 17:43:09 +01003213 msg(_("'redrawtime' exceeded, syntax highlighting disabled"));
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003214 }
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003215#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003216
3217 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003218 {
3219 rmp->startpos[0].lnum += lnum;
3220 rmp->endpos[0].lnum += lnum;
3221 return TRUE;
3222 }
3223 return FALSE;
3224}
3225
3226/*
3227 * Check one position in a line for a matching keyword.
3228 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003229 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003230 */
3231 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003232check_keyword_id(
3233 char_u *line,
3234 int startcol, /* position in line to check for keyword */
3235 int *endcolp, /* return: character after found keyword */
3236 long *flagsp, /* return: flags of matching keyword */
3237 short **next_listp, /* return: next_list of matching keyword */
3238 stateitem_T *cur_si, /* item at the top of the stack */
3239 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003240{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003241 keyentry_T *kp;
3242 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003243 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003244 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003245 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003246 hashtab_T *ht;
3247 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003248
3249 /* Find first character after the keyword. First character was already
3250 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003251 kwp = line + startcol;
3252 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003253 do
3254 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003255 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003256 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003257 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00003258 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003259 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003260 while (vim_iswordp_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
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003319syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003320{
3321#ifdef FEAT_CONCEAL
3322 char_u *arg = eap->arg;
3323 char_u *next;
3324
3325 eap->nextcmd = find_nextcmd(arg);
3326 if (eap->skip)
3327 return;
3328
3329 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003330 if (*arg == NUL)
3331 {
3332 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003333 msg(_("syntax conceal on"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003334 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01003335 msg(_("syntax conceal off"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003336 }
3337 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003338 curwin->w_s->b_syn_conceal = TRUE;
3339 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3340 curwin->w_s->b_syn_conceal = FALSE;
3341 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003342 semsg(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003343#endif
3344}
3345
3346/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003347 * Handle ":syntax case" command.
3348 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003349 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003350syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003351{
3352 char_u *arg = eap->arg;
3353 char_u *next;
3354
3355 eap->nextcmd = find_nextcmd(arg);
3356 if (eap->skip)
3357 return;
3358
3359 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003360 if (*arg == NUL)
3361 {
3362 if (curwin->w_s->b_syn_ic)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003363 msg(_("syntax case ignore"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003364 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01003365 msg(_("syntax case match"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003366 }
3367 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003368 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003369 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003370 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003371 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003372 semsg(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003373}
3374
3375/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003376 * Handle ":syntax spell" command.
3377 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003378 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003379syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003380{
3381 char_u *arg = eap->arg;
3382 char_u *next;
3383
3384 eap->nextcmd = find_nextcmd(arg);
3385 if (eap->skip)
3386 return;
3387
3388 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003389 if (*arg == NUL)
3390 {
3391 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003392 msg(_("syntax spell toplevel"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003393 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003394 msg(_("syntax spell notoplevel"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003395 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01003396 msg(_("syntax spell default"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003397 }
3398 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003399 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003400 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003401 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003402 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003403 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003404 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003405 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003406 semsg(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003407 return;
3408 }
3409
3410 /* assume spell checking changed, force a redraw */
3411 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003412}
3413
3414/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003415 * Handle ":syntax iskeyword" command.
3416 */
3417 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003418syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003419{
3420 char_u *arg = eap->arg;
3421 char_u save_chartab[32];
3422 char_u *save_isk;
3423
3424 if (eap->skip)
3425 return;
3426
3427 arg = skipwhite(arg);
3428 if (*arg == NUL)
3429 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003430 msg_puts("\n");
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003431 if (curwin->w_s->b_syn_isk != empty_option)
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003432 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003433 msg_puts(_("syntax iskeyword "));
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003434 msg_outtrans(curwin->w_s->b_syn_isk);
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003435 }
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003436 else
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003437 msg_outtrans((char_u *)_("syntax iskeyword not set"));
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003438 }
3439 else
3440 {
3441 if (STRNICMP(arg, "clear", 5) == 0)
3442 {
3443 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3444 (size_t)32);
3445 clear_string_option(&curwin->w_s->b_syn_isk);
3446 }
3447 else
3448 {
3449 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3450 save_isk = curbuf->b_p_isk;
3451 curbuf->b_p_isk = vim_strsave(arg);
3452
3453 buf_init_chartab(curbuf, FALSE);
3454 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3455 (size_t)32);
3456 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3457 clear_string_option(&curwin->w_s->b_syn_isk);
3458 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3459 curbuf->b_p_isk = save_isk;
3460 }
3461 }
3462 redraw_win_later(curwin, NOT_VALID);
3463}
3464
3465/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003466 * Clear all syntax info for one buffer.
3467 */
3468 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003469syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003470{
3471 int i;
3472
Bram Moolenaar860cae12010-06-05 23:22:07 +02003473 block->b_syn_error = FALSE; /* clear previous error */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003474#ifdef FEAT_RELTIME
3475 block->b_syn_slow = FALSE; /* clear previous timeout */
3476#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003477 block->b_syn_ic = FALSE; /* Use case, by default */
3478 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3479 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003480#ifdef FEAT_CONCEAL
3481 block->b_syn_conceal = FALSE;
3482#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003483
3484 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003485 clear_keywtab(&block->b_keywtab);
3486 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003487
3488 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003489 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3490 syn_clear_pattern(block, i);
3491 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003492
3493 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003494 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3495 syn_clear_cluster(block, i);
3496 ga_clear(&block->b_syn_clusters);
3497 block->b_spell_cluster_id = 0;
3498 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003499
Bram Moolenaar860cae12010-06-05 23:22:07 +02003500 block->b_syn_sync_flags = 0;
3501 block->b_syn_sync_minlines = 0;
3502 block->b_syn_sync_maxlines = 0;
3503 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003504
Bram Moolenaar473de612013-06-08 18:19:48 +02003505 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003506 block->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003507 VIM_CLEAR(block->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003508#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003509 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003510#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003511 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003512
3513 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003514 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003515 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003516
3517 /* Reset the counter for ":syn include" */
3518 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003519}
3520
3521/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003522 * Get rid of ownsyntax for window "wp".
3523 */
3524 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003525reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003526{
3527 if (wp->w_s != &wp->w_buffer->b_s)
3528 {
3529 syntax_clear(wp->w_s);
3530 vim_free(wp->w_s);
3531 wp->w_s = &wp->w_buffer->b_s;
3532 }
3533}
3534
3535/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003536 * Clear syncing info for one buffer.
3537 */
3538 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003539syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003540{
3541 int i;
3542
3543 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003544 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3545 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3546 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003547
Bram Moolenaar860cae12010-06-05 23:22:07 +02003548 curwin->w_s->b_syn_sync_flags = 0;
3549 curwin->w_s->b_syn_sync_minlines = 0;
3550 curwin->w_s->b_syn_sync_maxlines = 0;
3551 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003552
Bram Moolenaar473de612013-06-08 18:19:48 +02003553 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003554 curwin->w_s->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003555 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003556 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003557
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003558 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003559}
3560
3561/*
3562 * Remove one pattern from the buffer's pattern list.
3563 */
3564 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003565syn_remove_pattern(
3566 synblock_T *block,
3567 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003568{
3569 synpat_T *spp;
3570
Bram Moolenaar860cae12010-06-05 23:22:07 +02003571 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003572#ifdef FEAT_FOLDING
3573 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003574 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003575#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003576 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003577 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003578 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3579 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003580}
3581
3582/*
3583 * Clear and free one syntax pattern. When clearing all, must be called from
3584 * last to first!
3585 */
3586 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003587syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003588{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003589 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003590 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003591 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003592 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003593 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003594 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3595 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3596 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003597 }
3598}
3599
3600/*
3601 * Clear and free one syntax cluster.
3602 */
3603 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003604syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003605{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003606 vim_free(SYN_CLSTR(block)[i].scl_name);
3607 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3608 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003609}
3610
3611/*
3612 * Handle ":syntax clear" command.
3613 */
3614 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003615syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003616{
3617 char_u *arg = eap->arg;
3618 char_u *arg_end;
3619 int id;
3620
3621 eap->nextcmd = find_nextcmd(arg);
3622 if (eap->skip)
3623 return;
3624
3625 /*
3626 * We have to disable this within ":syn include @group filename",
3627 * because otherwise @group would get deleted.
3628 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3629 * clear".
3630 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003631 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003632 return;
3633
3634 if (ends_excmd(*arg))
3635 {
3636 /*
3637 * No argument: Clear all syntax items.
3638 */
3639 if (syncing)
3640 syntax_sync_clear();
3641 else
3642 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003643 syntax_clear(curwin->w_s);
3644 if (curwin->w_s == &curwin->w_buffer->b_s)
3645 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003646 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003647 }
3648 }
3649 else
3650 {
3651 /*
3652 * Clear the group IDs that are in the argument.
3653 */
3654 while (!ends_excmd(*arg))
3655 {
3656 arg_end = skiptowhite(arg);
3657 if (*arg == '@')
3658 {
3659 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3660 if (id == 0)
3661 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003662 semsg(_("E391: No such syntax cluster: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003663 break;
3664 }
3665 else
3666 {
3667 /*
3668 * We can't physically delete a cluster without changing
3669 * the IDs of other clusters, so we do the next best thing
3670 * and make it empty.
3671 */
3672 short scl_id = id - SYNID_CLUSTER;
3673
Bram Moolenaard23a8232018-02-10 18:45:26 +01003674 VIM_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003675 }
3676 }
3677 else
3678 {
3679 id = syn_namen2id(arg, (int)(arg_end - arg));
3680 if (id == 0)
3681 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003682 semsg(_(e_nogroup), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003683 break;
3684 }
3685 else
3686 syn_clear_one(id, syncing);
3687 }
3688 arg = skipwhite(arg_end);
3689 }
3690 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003691 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003692 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003693}
3694
3695/*
3696 * Clear one syntax group for the current buffer.
3697 */
3698 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003699syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003700{
3701 synpat_T *spp;
3702 int idx;
3703
3704 /* Clear keywords only when not ":syn sync clear group-name" */
3705 if (!syncing)
3706 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003707 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3708 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003709 }
3710
3711 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003712 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003713 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003714 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003715 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3716 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003717 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003718 }
3719}
3720
3721/*
3722 * Handle ":syntax on" command.
3723 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003724 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003725syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003726{
3727 syn_cmd_onoff(eap, "syntax");
3728}
3729
3730/*
3731 * Handle ":syntax enable" command.
3732 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003733 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003734syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003735{
3736 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3737 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003738 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003739}
3740
3741/*
3742 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003743 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003744 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003745 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003746syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003747{
3748 eap->nextcmd = check_nextcmd(eap->arg);
3749 if (!eap->skip)
3750 {
3751 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3752 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003753 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003754 }
3755}
3756
3757/*
3758 * Handle ":syntax manual" command.
3759 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003760 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003761syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003762{
3763 syn_cmd_onoff(eap, "manual");
3764}
3765
3766/*
3767 * Handle ":syntax off" command.
3768 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003769 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003770syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003771{
3772 syn_cmd_onoff(eap, "nosyntax");
3773}
3774
3775 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003776syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003777{
3778 char_u buf[100];
3779
3780 eap->nextcmd = check_nextcmd(eap->arg);
3781 if (!eap->skip)
3782 {
3783 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003784 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003785 do_cmdline_cmd(buf);
3786 }
3787}
3788
3789/*
3790 * Handle ":syntax [list]" command: list current syntax words.
3791 */
3792 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003793syn_cmd_list(
3794 exarg_T *eap,
3795 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003796{
3797 char_u *arg = eap->arg;
3798 int id;
3799 char_u *arg_end;
3800
3801 eap->nextcmd = find_nextcmd(arg);
3802 if (eap->skip)
3803 return;
3804
Bram Moolenaar860cae12010-06-05 23:22:07 +02003805 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003806 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003807 msg(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003808 return;
3809 }
3810
3811 if (syncing)
3812 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003813 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003814 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003815 msg_puts(_("syncing on C-style comments"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003816 syn_lines_msg();
3817 syn_match_msg();
3818 return;
3819 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003820 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003821 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003822 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003823 msg_puts(_("no syncing"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003824 else
3825 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003826 msg_puts(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003827 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar32526b32019-01-19 17:43:09 +01003828 msg_puts(_(" lines before top line"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003829 syn_match_msg();
3830 }
3831 return;
3832 }
Bram Moolenaar32526b32019-01-19 17:43:09 +01003833 msg_puts_title(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003834 if (curwin->w_s->b_syn_sync_minlines > 0
3835 || curwin->w_s->b_syn_sync_maxlines > 0
3836 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003837 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003838 msg_puts(_("\nsyncing on items"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003839 syn_lines_msg();
3840 syn_match_msg();
3841 }
3842 }
3843 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01003844 msg_puts_title(_("\n--- Syntax items ---"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003845 if (ends_excmd(*arg))
3846 {
3847 /*
3848 * No argument: List all group IDs and all syntax clusters.
3849 */
3850 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3851 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003852 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003853 syn_list_cluster(id);
3854 }
3855 else
3856 {
3857 /*
3858 * List the group IDs and syntax clusters that are in the argument.
3859 */
3860 while (!ends_excmd(*arg) && !got_int)
3861 {
3862 arg_end = skiptowhite(arg);
3863 if (*arg == '@')
3864 {
3865 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3866 if (id == 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003867 semsg(_("E392: No such syntax cluster: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003868 else
3869 syn_list_cluster(id - SYNID_CLUSTER);
3870 }
3871 else
3872 {
3873 id = syn_namen2id(arg, (int)(arg_end - arg));
3874 if (id == 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003875 semsg(_(e_nogroup), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003876 else
3877 syn_list_one(id, syncing, TRUE);
3878 }
3879 arg = skipwhite(arg_end);
3880 }
3881 }
3882 eap->nextcmd = check_nextcmd(arg);
3883}
3884
3885 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003886syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003887{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003888 if (curwin->w_s->b_syn_sync_maxlines > 0
3889 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003890 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003891 msg_puts("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003892 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003893 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003894 msg_puts(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003895 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3896 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003897 msg_puts(", ");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003898 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003899 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003900 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003901 msg_puts(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003902 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003903 }
Bram Moolenaar32526b32019-01-19 17:43:09 +01003904 msg_puts(_(" lines before top line"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003905 }
3906}
3907
3908 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003909syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003910{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003911 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003912 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003913 msg_puts(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003914 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar32526b32019-01-19 17:43:09 +01003915 msg_puts(_(" line breaks"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003916 }
3917}
3918
3919static int last_matchgroup;
3920
3921struct name_list
3922{
3923 int flag;
3924 char *name;
3925};
3926
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01003927static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003928
3929/*
3930 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3931 */
3932 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003933syn_list_one(
3934 int id,
3935 int syncing, /* when TRUE: list syncing items */
3936 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003937{
3938 int attr;
3939 int idx;
3940 int did_header = FALSE;
3941 synpat_T *spp;
3942 static struct name_list namelist1[] =
3943 {
3944 {HL_DISPLAY, "display"},
3945 {HL_CONTAINED, "contained"},
3946 {HL_ONELINE, "oneline"},
3947 {HL_KEEPEND, "keepend"},
3948 {HL_EXTEND, "extend"},
3949 {HL_EXCLUDENL, "excludenl"},
3950 {HL_TRANSP, "transparent"},
3951 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003952#ifdef FEAT_CONCEAL
3953 {HL_CONCEAL, "conceal"},
3954 {HL_CONCEALENDS, "concealends"},
3955#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003956 {0, NULL}
3957 };
3958 static struct name_list namelist2[] =
3959 {
3960 {HL_SKIPWHITE, "skipwhite"},
3961 {HL_SKIPNL, "skipnl"},
3962 {HL_SKIPEMPTY, "skipempty"},
3963 {0, NULL}
3964 };
3965
Bram Moolenaar8820b482017-03-16 17:23:31 +01003966 attr = HL_ATTR(HLF_D); /* highlight like directories */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003967
3968 /* list the keywords for "id" */
3969 if (!syncing)
3970 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003971 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3972 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003973 did_header, attr);
3974 }
3975
3976 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003977 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003978 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003979 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003980 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3981 continue;
3982
3983 (void)syn_list_header(did_header, 999, id);
3984 did_header = TRUE;
3985 last_matchgroup = 0;
3986 if (spp->sp_type == SPTYPE_MATCH)
3987 {
3988 put_pattern("match", ' ', spp, attr);
3989 msg_putchar(' ');
3990 }
3991 else if (spp->sp_type == SPTYPE_START)
3992 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003993 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
3994 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3995 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
3996 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3997 while (idx < curwin->w_s->b_syn_patterns.ga_len
3998 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
3999 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004000 --idx;
4001 msg_putchar(' ');
4002 }
4003 syn_list_flags(namelist1, spp->sp_flags, attr);
4004
4005 if (spp->sp_cont_list != NULL)
4006 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4007
4008 if (spp->sp_syn.cont_in_list != NULL)
4009 put_id_list((char_u *)"containedin",
4010 spp->sp_syn.cont_in_list, attr);
4011
4012 if (spp->sp_next_list != NULL)
4013 {
4014 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4015 syn_list_flags(namelist2, spp->sp_flags, attr);
4016 }
4017 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4018 {
4019 if (spp->sp_flags & HL_SYNC_HERE)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004020 msg_puts_attr("grouphere", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004021 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004022 msg_puts_attr("groupthere", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004023 msg_putchar(' ');
4024 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004025 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004026 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4027 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004028 msg_puts("NONE");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004029 msg_putchar(' ');
4030 }
4031 }
4032
4033 /* list the link, if there is one */
4034 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4035 {
4036 (void)syn_list_header(did_header, 999, id);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004037 msg_puts_attr("links to", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004038 msg_putchar(' ');
4039 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4040 }
4041}
4042
4043 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004044syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004045{
4046 int i;
4047
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004048 for (i = 0; nlist[i].flag != 0; ++i)
4049 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004050 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004051 msg_puts_attr(nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004052 msg_putchar(' ');
4053 }
4054}
4055
4056/*
4057 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4058 */
4059 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004060syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004061{
4062 int endcol = 15;
4063
4064 /* slight hack: roughly duplicate the guts of syn_list_header() */
4065 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004066 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004067
4068 if (msg_col >= endcol) /* output at least one space */
4069 endcol = msg_col + 1;
4070 if (Columns <= endcol) /* avoid hang for tiny window */
4071 endcol = Columns - 1;
4072
4073 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004074 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004075 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004076 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004077 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004078 }
4079 else
4080 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004081 msg_puts_attr("cluster", HL_ATTR(HLF_D));
4082 msg_puts("=NONE");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004083 }
4084}
4085
4086 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004087put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004088{
4089 short *p;
4090
Bram Moolenaar32526b32019-01-19 17:43:09 +01004091 msg_puts_attr((char *)name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004092 msg_putchar('=');
4093 for (p = list; *p; ++p)
4094 {
4095 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4096 {
4097 if (p[1])
Bram Moolenaar32526b32019-01-19 17:43:09 +01004098 msg_puts("ALLBUT");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004099 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004100 msg_puts("ALL");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004101 }
4102 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4103 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004104 msg_puts("TOP");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004105 }
4106 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4107 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004108 msg_puts("CONTAINED");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004109 }
4110 else if (*p >= SYNID_CLUSTER)
4111 {
4112 short scl_id = *p - SYNID_CLUSTER;
4113
4114 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004115 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004116 }
4117 else
4118 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4119 if (p[1])
4120 msg_putchar(',');
4121 }
4122 msg_putchar(' ');
4123}
4124
4125 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004126put_pattern(
4127 char *s,
4128 int c,
4129 synpat_T *spp,
4130 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004131{
4132 long n;
4133 int mask;
4134 int first;
4135 static char *sepchars = "/+=-#@\"|'^&";
4136 int i;
4137
4138 /* May have to write "matchgroup=group" */
4139 if (last_matchgroup != spp->sp_syn_match_id)
4140 {
4141 last_matchgroup = spp->sp_syn_match_id;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004142 msg_puts_attr("matchgroup", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004143 msg_putchar('=');
4144 if (last_matchgroup == 0)
4145 msg_outtrans((char_u *)"NONE");
4146 else
4147 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4148 msg_putchar(' ');
4149 }
4150
4151 /* output the name of the pattern and an '=' or ' ' */
Bram Moolenaar32526b32019-01-19 17:43:09 +01004152 msg_puts_attr(s, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004153 msg_putchar(c);
4154
4155 /* output the pattern, in between a char that is not in the pattern */
4156 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4157 if (sepchars[++i] == NUL)
4158 {
4159 i = 0; /* no good char found, just use the first one */
4160 break;
4161 }
4162 msg_putchar(sepchars[i]);
4163 msg_outtrans(spp->sp_pattern);
4164 msg_putchar(sepchars[i]);
4165
4166 /* output any pattern options */
4167 first = TRUE;
4168 for (i = 0; i < SPO_COUNT; ++i)
4169 {
4170 mask = (1 << i);
4171 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4172 {
4173 if (!first)
4174 msg_putchar(','); /* separate with commas */
Bram Moolenaar32526b32019-01-19 17:43:09 +01004175 msg_puts(spo_name_tab[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004176 n = spp->sp_offsets[i];
4177 if (i != SPO_LC_OFF)
4178 {
4179 if (spp->sp_off_flags & mask)
4180 msg_putchar('s');
4181 else
4182 msg_putchar('e');
4183 if (n > 0)
4184 msg_putchar('+');
4185 }
4186 if (n || i == SPO_LC_OFF)
4187 msg_outnum(n);
4188 first = FALSE;
4189 }
4190 }
4191 msg_putchar(' ');
4192}
4193
4194/*
4195 * List or clear the keywords for one syntax group.
4196 * Return TRUE if the header has been printed.
4197 */
4198 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004199syn_list_keywords(
4200 int id,
4201 hashtab_T *ht,
4202 int did_header, /* header has already been printed */
4203 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004204{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004205 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004206 hashitem_T *hi;
4207 keyentry_T *kp;
4208 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004209 int prev_contained = 0;
4210 short *prev_next_list = NULL;
4211 short *prev_cont_in_list = NULL;
4212 int prev_skipnl = 0;
4213 int prev_skipwhite = 0;
4214 int prev_skipempty = 0;
4215
Bram Moolenaar071d4272004-06-13 20:20:40 +00004216 /*
4217 * Unfortunately, this list of keywords is not sorted on alphabet but on
4218 * hash value...
4219 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004220 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004221 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004222 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004223 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004224 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004225 --todo;
4226 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004227 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004228 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004229 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004230 if (prev_contained != (kp->flags & HL_CONTAINED)
4231 || prev_skipnl != (kp->flags & HL_SKIPNL)
4232 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4233 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4234 || prev_cont_in_list != kp->k_syn.cont_in_list
4235 || prev_next_list != kp->next_list)
4236 outlen = 9999;
4237 else
4238 outlen = (int)STRLEN(kp->keyword);
4239 /* output "contained" and "nextgroup" on each line */
4240 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004241 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004242 prev_contained = 0;
4243 prev_next_list = NULL;
4244 prev_cont_in_list = NULL;
4245 prev_skipnl = 0;
4246 prev_skipwhite = 0;
4247 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004248 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004249 did_header = TRUE;
4250 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004251 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004252 msg_puts_attr("contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004253 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004254 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004255 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004256 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004257 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004258 put_id_list((char_u *)"containedin",
4259 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004260 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004261 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004262 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004263 if (kp->next_list != prev_next_list)
4264 {
4265 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4266 msg_putchar(' ');
4267 prev_next_list = kp->next_list;
4268 if (kp->flags & HL_SKIPNL)
4269 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004270 msg_puts_attr("skipnl", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004271 msg_putchar(' ');
4272 prev_skipnl = (kp->flags & HL_SKIPNL);
4273 }
4274 if (kp->flags & HL_SKIPWHITE)
4275 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004276 msg_puts_attr("skipwhite", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004277 msg_putchar(' ');
4278 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4279 }
4280 if (kp->flags & HL_SKIPEMPTY)
4281 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004282 msg_puts_attr("skipempty", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004283 msg_putchar(' ');
4284 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4285 }
4286 }
4287 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004288 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004289 }
4290 }
4291 }
4292
4293 return did_header;
4294}
4295
4296 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004297syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004298{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004299 hashitem_T *hi;
4300 keyentry_T *kp;
4301 keyentry_T *kp_prev;
4302 keyentry_T *kp_next;
4303 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004304
Bram Moolenaardad6b692005-01-25 22:14:34 +00004305 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004306 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004307 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004308 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004309 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004310 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004311 --todo;
4312 kp_prev = NULL;
4313 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004314 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004315 if (kp->k_syn.id == id)
4316 {
4317 kp_next = kp->ke_next;
4318 if (kp_prev == NULL)
4319 {
4320 if (kp_next == NULL)
4321 hash_remove(ht, hi);
4322 else
4323 hi->hi_key = KE2HIKEY(kp_next);
4324 }
4325 else
4326 kp_prev->ke_next = kp_next;
4327 vim_free(kp->next_list);
4328 vim_free(kp->k_syn.cont_in_list);
4329 vim_free(kp);
4330 kp = kp_next;
4331 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004332 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004333 {
4334 kp_prev = kp;
4335 kp = kp->ke_next;
4336 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004337 }
4338 }
4339 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004340 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004341}
4342
4343/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004344 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004345 */
4346 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004347clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004348{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004349 hashitem_T *hi;
4350 int todo;
4351 keyentry_T *kp;
4352 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004354 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004355 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004356 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004357 if (!HASHITEM_EMPTY(hi))
4358 {
4359 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004360 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004361 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004362 kp_next = kp->ke_next;
4363 vim_free(kp->next_list);
4364 vim_free(kp->k_syn.cont_in_list);
4365 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004366 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004367 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004368 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004369 hash_clear(ht);
4370 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004371}
4372
4373/*
4374 * Add a keyword to the list of keywords.
4375 */
4376 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004377add_keyword(
4378 char_u *name, /* name of keyword */
4379 int id, /* group ID for this keyword */
4380 int flags, /* flags for this keyword */
4381 short *cont_in_list, /* containedin for this keyword */
4382 short *next_list, /* nextgroup for this keyword */
4383 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004384{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004385 keyentry_T *kp;
4386 hashtab_T *ht;
4387 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004388 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004389 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004390 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004391
Bram Moolenaar860cae12010-06-05 23:22:07 +02004392 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004393 name_ic = str_foldcase(name, (int)STRLEN(name),
4394 name_folded, MAXKEYWLEN + 1);
4395 else
4396 name_ic = name;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004397 kp = alloc(sizeof(keyentry_T) + STRLEN(name_ic));
Bram Moolenaardad6b692005-01-25 22:14:34 +00004398 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004399 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004400 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004401 kp->k_syn.id = id;
4402 kp->k_syn.inc_tag = current_syn_inc_tag;
4403 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004404 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004405 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004406 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004407 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004408 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004409
Bram Moolenaar860cae12010-06-05 23:22:07 +02004410 if (curwin->w_s->b_syn_ic)
4411 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004412 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004413 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004414
Bram Moolenaardad6b692005-01-25 22:14:34 +00004415 hash = hash_hash(kp->keyword);
4416 hi = hash_lookup(ht, kp->keyword, hash);
4417 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004418 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004419 /* new keyword, add to hashtable */
4420 kp->ke_next = NULL;
4421 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004422 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004423 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004424 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004425 /* keyword already exists, prepend to list */
4426 kp->ke_next = HI2KE(hi);
4427 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004428 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004429}
4430
4431/*
4432 * Get the start and end of the group name argument.
4433 * Return a pointer to the first argument.
4434 * Return NULL if the end of the command was found instead of further args.
4435 */
4436 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004437get_group_name(
4438 char_u *arg, /* start of the argument */
4439 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004440{
4441 char_u *rest;
4442
4443 *name_end = skiptowhite(arg);
4444 rest = skipwhite(*name_end);
4445
4446 /*
4447 * Check if there are enough arguments. The first argument may be a
4448 * pattern, where '|' is allowed, so only check for NUL.
4449 */
4450 if (ends_excmd(*arg) || *rest == NUL)
4451 return NULL;
4452 return rest;
4453}
4454
4455/*
4456 * Check for syntax command option arguments.
4457 * This can be called at any place in the list of arguments, and just picks
4458 * out the arguments that are known. Can be called several times in a row to
4459 * collect all options in between other arguments.
4460 * Return a pointer to the next argument (which isn't an option).
4461 * Return NULL for any error;
4462 */
4463 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004464get_syn_options(
4465 char_u *arg, /* next argument to be checked */
4466 syn_opt_arg_T *opt, /* various things */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004467 int *conceal_char UNUSED,
4468 int skip) /* TRUE if skipping over command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004469{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004470 char_u *gname_start, *gname;
4471 int syn_id;
4472 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004473 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004474 int i;
4475 int fidx;
4476 static struct flag
4477 {
4478 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004479 int argtype;
4480 int flags;
4481 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4482 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4483 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4484 {"eExXtTeEnNdD", 0, HL_EXTEND},
4485 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4486 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4487 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4488 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4489 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4490 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4491 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4492 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4493 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004494 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4495 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4496 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004497 {"cCoOnNtTaAiInNsS", 1, 0},
4498 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4499 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004500 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004501 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004502
4503 if (arg == NULL) /* already detected error */
4504 return NULL;
4505
Bram Moolenaar860cae12010-06-05 23:22:07 +02004506#ifdef FEAT_CONCEAL
4507 if (curwin->w_s->b_syn_conceal)
4508 opt->flags |= HL_CONCEAL;
4509#endif
4510
Bram Moolenaar071d4272004-06-13 20:20:40 +00004511 for (;;)
4512 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004513 /*
4514 * This is used very often when a large number of keywords is defined.
4515 * Need to skip quickly when no option name is found.
4516 * Also avoid tolower(), it's slow.
4517 */
4518 if (strchr(first_letters, *arg) == NULL)
4519 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004520
4521 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4522 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004523 p = flagtab[fidx].name;
4524 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4525 if (arg[len] != p[i] && arg[len] != p[i + 1])
4526 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004527 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004528 || (flagtab[fidx].argtype > 0
4529 ? arg[len] == '='
4530 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004531 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004532 if (opt->keyword
4533 && (flagtab[fidx].flags == HL_DISPLAY
4534 || flagtab[fidx].flags == HL_FOLD
4535 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004536 /* treat "display", "fold" and "extend" as a keyword */
4537 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004538 break;
4539 }
4540 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004541 if (fidx < 0) /* no match found */
4542 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004543
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004544 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004545 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004546 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004547 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004548 emsg(_("E395: contains argument not accepted here"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004549 return NULL;
4550 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004551 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004552 return NULL;
4553 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004554 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004555 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004556 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004557 return NULL;
4558 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004559 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004560 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004561 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004562 return NULL;
4563 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004564 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4565 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004566 /* cchar=? */
4567 if (has_mbyte)
4568 {
Bram Moolenaar264b74f2019-01-24 17:18:42 +01004569#ifdef FEAT_CONCEAL
Bram Moolenaar860cae12010-06-05 23:22:07 +02004570 *conceal_char = mb_ptr2char(arg + 6);
Bram Moolenaar264b74f2019-01-24 17:18:42 +01004571#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004572 arg += mb_ptr2len(arg + 6) - 1;
4573 }
4574 else
Bram Moolenaar56be9502010-06-06 14:20:26 +02004575 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004576#ifdef FEAT_CONCEAL
4577 *conceal_char = arg[6];
4578#else
4579 ;
4580#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004581 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004582#ifdef FEAT_CONCEAL
4583 if (!vim_isprintc_strict(*conceal_char))
4584 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004585 emsg(_("E844: invalid cchar value"));
Bram Moolenaar4124e722011-01-22 00:58:20 +01004586 return NULL;
4587 }
4588#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004589 arg = skipwhite(arg + 7);
4590 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004591 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004592 {
4593 opt->flags |= flagtab[fidx].flags;
4594 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004595
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004596 if (flagtab[fidx].flags == HL_SYNC_HERE
4597 || flagtab[fidx].flags == HL_SYNC_THERE)
4598 {
4599 if (opt->sync_idx == NULL)
4600 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004601 emsg(_("E393: group[t]here not accepted here"));
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004602 return NULL;
4603 }
4604 gname_start = arg;
4605 arg = skiptowhite(arg);
4606 if (gname_start == arg)
4607 return NULL;
4608 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4609 if (gname == NULL)
4610 return NULL;
4611 if (STRCMP(gname, "NONE") == 0)
4612 *opt->sync_idx = NONE_IDX;
4613 else
4614 {
4615 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004616 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4617 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4618 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004619 {
4620 *opt->sync_idx = i;
4621 break;
4622 }
4623 if (i < 0)
4624 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004625 semsg(_("E394: Didn't find region item for %s"), gname);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004626 vim_free(gname);
4627 return NULL;
4628 }
4629 }
4630
4631 vim_free(gname);
4632 arg = skipwhite(arg);
4633 }
4634#ifdef FEAT_FOLDING
4635 else if (flagtab[fidx].flags == HL_FOLD
4636 && foldmethodIsSyntax(curwin))
4637 /* Need to update folds later. */
4638 foldUpdateAll(curwin);
4639#endif
4640 }
4641 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004642
4643 return arg;
4644}
4645
4646/*
4647 * Adjustments to syntax item when declared in a ":syn include"'d file.
4648 * Set the contained flag, and if the item is not already contained, add it
4649 * to the specified top-level group, if any.
4650 */
4651 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004652syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004653{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004654 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004655 return;
4656 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004657 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004658 {
4659 /* We have to alloc this, because syn_combine_list() will free it. */
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004660 short *grp_list = ALLOC_MULT(short, 2);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004661 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004662
4663 if (grp_list != NULL)
4664 {
4665 grp_list[0] = id;
4666 grp_list[1] = 0;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004667 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list,
4668 &grp_list, CLUSTER_ADD);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004669 }
4670 }
4671}
4672
4673/*
4674 * Handle ":syntax include [@{group-name}] filename" command.
4675 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004676 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004677syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004678{
4679 char_u *arg = eap->arg;
4680 int sgl_id = 1;
4681 char_u *group_name_end;
4682 char_u *rest;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004683 char *errormsg = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004684 int prev_toplvl_grp;
4685 int prev_syn_inc_tag;
4686 int source = FALSE;
4687
4688 eap->nextcmd = find_nextcmd(arg);
4689 if (eap->skip)
4690 return;
4691
4692 if (arg[0] == '@')
4693 {
4694 ++arg;
4695 rest = get_group_name(arg, &group_name_end);
4696 if (rest == NULL)
4697 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004698 emsg(_("E397: Filename required"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004699 return;
4700 }
4701 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004702 if (sgl_id == 0)
4703 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004704 /* separate_nextcmd() and expand_filename() depend on this */
4705 eap->arg = rest;
4706 }
4707
4708 /*
4709 * Everything that's left, up to the next command, should be the
4710 * filename to include.
4711 */
Bram Moolenaar8071cb22019-07-12 17:58:01 +02004712 eap->argt |= (EX_XFILE | EX_NOSPC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004713 separate_nextcmd(eap);
4714 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4715 {
4716 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4717 * file. Need to expand the file name first. In other cases
4718 * ":runtime!" is used. */
4719 source = TRUE;
4720 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4721 {
4722 if (errormsg != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004723 emsg(errormsg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004724 return;
4725 }
4726 }
4727
4728 /*
4729 * Save and restore the existing top-level grouplist id and ":syn
4730 * include" tag around the actual inclusion.
4731 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004732 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4733 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004734 emsg(_("E847: Too many syntax includes"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004735 return;
4736 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004737 prev_syn_inc_tag = current_syn_inc_tag;
4738 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004739 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4740 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004741 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004742 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004743 semsg(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004744 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004745 current_syn_inc_tag = prev_syn_inc_tag;
4746}
4747
4748/*
4749 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4750 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004751 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004752syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004753{
4754 char_u *arg = eap->arg;
4755 char_u *group_name_end;
4756 int syn_id;
4757 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004758 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004759 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004760 char_u *kw;
4761 syn_opt_arg_T syn_opt_arg;
4762 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004763 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004764
4765 rest = get_group_name(arg, &group_name_end);
4766
4767 if (rest != NULL)
4768 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004769 if (eap->skip)
4770 syn_id = -1;
4771 else
4772 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004773 if (syn_id != 0)
4774 /* allocate a buffer, for removing backslashes in the keyword */
Bram Moolenaar964b3742019-05-24 18:54:09 +02004775 keyword_copy = alloc(STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004776 if (keyword_copy != NULL)
4777 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004778 syn_opt_arg.flags = 0;
4779 syn_opt_arg.keyword = TRUE;
4780 syn_opt_arg.sync_idx = NULL;
4781 syn_opt_arg.has_cont_list = FALSE;
4782 syn_opt_arg.cont_in_list = NULL;
4783 syn_opt_arg.next_list = NULL;
4784
Bram Moolenaar071d4272004-06-13 20:20:40 +00004785 /*
4786 * The options given apply to ALL keywords, so all options must be
4787 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004788 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004789 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004790 cnt = 0;
4791 p = keyword_copy;
4792 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004793 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004794 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4795 eap->skip);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004796 if (rest == NULL || ends_excmd(*rest))
4797 break;
4798 /* Copy the keyword, removing backslashes, and add a NUL. */
Bram Moolenaar1c465442017-03-12 20:10:05 +01004799 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004800 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004801 if (*rest == '\\' && rest[1] != NUL)
4802 ++rest;
4803 *p++ = *rest++;
4804 }
4805 *p++ = NUL;
4806 ++cnt;
4807 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004808
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004809 if (!eap->skip)
4810 {
4811 /* Adjust flags for use of ":syn include". */
4812 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4813
4814 /*
4815 * 2: Add an entry for each keyword.
4816 */
4817 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4818 {
4819 for (p = vim_strchr(kw, '['); ; )
4820 {
4821 if (p != NULL)
4822 *p = NUL;
4823 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004824 syn_opt_arg.cont_in_list,
4825 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004826 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004827 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004828 if (p[1] == NUL)
4829 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004830 semsg(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004831 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004832 }
4833 if (p[1] == ']')
4834 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004835 if (p[2] != NUL)
4836 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004837 semsg(_("E890: trailing char after ']': %s]%s"),
Bram Moolenaar1560d072015-08-13 22:53:29 +02004838 kw, &p[2]);
4839 goto error;
4840 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004841 kw = p + 1; /* skip over the "]" */
4842 break;
4843 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004844 if (has_mbyte)
4845 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004846 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004847
4848 mch_memmove(p, p + 1, l);
4849 p += l;
4850 }
4851 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004852 {
4853 p[0] = p[1];
4854 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004855 }
4856 }
4857 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004858 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004859error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004860 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004861 vim_free(syn_opt_arg.cont_in_list);
4862 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004863 }
4864 }
4865
4866 if (rest != NULL)
4867 eap->nextcmd = check_nextcmd(rest);
4868 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004869 semsg(_(e_invarg2), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004870
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004871 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004872 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004873}
4874
4875/*
4876 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4877 *
4878 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4879 */
4880 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004881syn_cmd_match(
4882 exarg_T *eap,
4883 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004884{
4885 char_u *arg = eap->arg;
4886 char_u *group_name_end;
4887 char_u *rest;
4888 synpat_T item; /* the item found in the line */
4889 int syn_id;
4890 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004891 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004892 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004893 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004894
4895 /* Isolate the group name, check for validity */
4896 rest = get_group_name(arg, &group_name_end);
4897
4898 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004899 syn_opt_arg.flags = 0;
4900 syn_opt_arg.keyword = FALSE;
4901 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4902 syn_opt_arg.has_cont_list = TRUE;
4903 syn_opt_arg.cont_list = NULL;
4904 syn_opt_arg.cont_in_list = NULL;
4905 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01004906 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004907
4908 /* get the pattern. */
4909 init_syn_patterns();
4910 vim_memset(&item, 0, sizeof(item));
4911 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004912 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4913 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004914
4915 /* Get options after the pattern */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004916 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004917
4918 if (rest != NULL) /* all arguments are valid */
4919 {
4920 /*
4921 * Check for trailing command and illegal trailing arguments.
4922 */
4923 eap->nextcmd = check_nextcmd(rest);
4924 if (!ends_excmd(*rest) || eap->skip)
4925 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004926 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004927 && (syn_id = syn_check_group(arg,
4928 (int)(group_name_end - arg))) != 0)
4929 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004930 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004931 /*
4932 * Store the pattern in the syn_items list
4933 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004934 idx = curwin->w_s->b_syn_patterns.ga_len;
4935 SYN_ITEMS(curwin->w_s)[idx] = item;
4936 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4937 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4938 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4939 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4940 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4941 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4942 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4943 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004944 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004945#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02004946 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004947#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004948 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004949 curwin->w_s->b_syn_containedin = TRUE;
4950 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4951 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004952
4953 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004954 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004955 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004956#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004957 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004958 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004959#endif
4960
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004961 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004962 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004963 return; /* don't free the progs and patterns now */
4964 }
4965 }
4966
4967 /*
4968 * Something failed, free the allocated memory.
4969 */
Bram Moolenaar473de612013-06-08 18:19:48 +02004970 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004971 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004972 vim_free(syn_opt_arg.cont_list);
4973 vim_free(syn_opt_arg.cont_in_list);
4974 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004975
4976 if (rest == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004977 semsg(_(e_invarg2), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004978}
4979
4980/*
4981 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4982 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4983 */
4984 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004985syn_cmd_region(
4986 exarg_T *eap,
4987 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004988{
4989 char_u *arg = eap->arg;
4990 char_u *group_name_end;
4991 char_u *rest; /* next arg, NULL on error */
4992 char_u *key_end;
4993 char_u *key = NULL;
4994 char_u *p;
4995 int item;
4996#define ITEM_START 0
4997#define ITEM_SKIP 1
4998#define ITEM_END 2
4999#define ITEM_MATCHGROUP 3
5000 struct pat_ptr
5001 {
5002 synpat_T *pp_synp; /* pointer to syn_pattern */
5003 int pp_matchgroup_id; /* matchgroup ID */
5004 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5005 } *(pat_ptrs[3]);
5006 /* patterns found in the line */
5007 struct pat_ptr *ppp;
5008 struct pat_ptr *ppp_next;
5009 int pat_count = 0; /* nr of syn_patterns found */
5010 int syn_id;
5011 int matchgroup_id = 0;
5012 int not_enough = FALSE; /* not enough arguments */
5013 int illegal = FALSE; /* illegal arguments */
5014 int success = FALSE;
5015 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005016 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005017 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005018
5019 /* Isolate the group name, check for validity */
5020 rest = get_group_name(arg, &group_name_end);
5021
5022 pat_ptrs[0] = NULL;
5023 pat_ptrs[1] = NULL;
5024 pat_ptrs[2] = NULL;
5025
5026 init_syn_patterns();
5027
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005028 syn_opt_arg.flags = 0;
5029 syn_opt_arg.keyword = FALSE;
5030 syn_opt_arg.sync_idx = NULL;
5031 syn_opt_arg.has_cont_list = TRUE;
5032 syn_opt_arg.cont_list = NULL;
5033 syn_opt_arg.cont_in_list = NULL;
5034 syn_opt_arg.next_list = NULL;
5035
Bram Moolenaar071d4272004-06-13 20:20:40 +00005036 /*
5037 * get the options, patterns and matchgroup.
5038 */
5039 while (rest != NULL && !ends_excmd(*rest))
5040 {
5041 /* Check for option arguments */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005042 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005043 if (rest == NULL || ends_excmd(*rest))
5044 break;
5045
5046 /* must be a pattern or matchgroup then */
5047 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005048 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005049 ++key_end;
5050 vim_free(key);
5051 key = vim_strnsave_up(rest, (int)(key_end - rest));
5052 if (key == NULL) /* out of memory */
5053 {
5054 rest = NULL;
5055 break;
5056 }
5057 if (STRCMP(key, "MATCHGROUP") == 0)
5058 item = ITEM_MATCHGROUP;
5059 else if (STRCMP(key, "START") == 0)
5060 item = ITEM_START;
5061 else if (STRCMP(key, "END") == 0)
5062 item = ITEM_END;
5063 else if (STRCMP(key, "SKIP") == 0)
5064 {
5065 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5066 {
5067 illegal = TRUE;
5068 break;
5069 }
5070 item = ITEM_SKIP;
5071 }
5072 else
5073 break;
5074 rest = skipwhite(key_end);
5075 if (*rest != '=')
5076 {
5077 rest = NULL;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005078 semsg(_("E398: Missing '=': %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005079 break;
5080 }
5081 rest = skipwhite(rest + 1);
5082 if (*rest == NUL)
5083 {
5084 not_enough = TRUE;
5085 break;
5086 }
5087
5088 if (item == ITEM_MATCHGROUP)
5089 {
5090 p = skiptowhite(rest);
5091 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5092 matchgroup_id = 0;
5093 else
5094 {
5095 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5096 if (matchgroup_id == 0)
5097 {
5098 illegal = TRUE;
5099 break;
5100 }
5101 }
5102 rest = skipwhite(p);
5103 }
5104 else
5105 {
5106 /*
5107 * Allocate room for a syn_pattern, and link it in the list of
5108 * syn_patterns for this item, at the start (because the list is
5109 * used from end to start).
5110 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005111 ppp = ALLOC_ONE(struct pat_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005112 if (ppp == NULL)
5113 {
5114 rest = NULL;
5115 break;
5116 }
5117 ppp->pp_next = pat_ptrs[item];
5118 pat_ptrs[item] = ppp;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005119 ppp->pp_synp = ALLOC_CLEAR_ONE(synpat_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005120 if (ppp->pp_synp == NULL)
5121 {
5122 rest = NULL;
5123 break;
5124 }
5125
5126 /*
5127 * Get the syntax pattern and the following offset(s).
5128 */
5129 /* Enable the appropriate \z specials. */
5130 if (item == ITEM_START)
5131 reg_do_extmatch = REX_SET;
5132 else if (item == ITEM_SKIP || item == ITEM_END)
5133 reg_do_extmatch = REX_USE;
5134 rest = get_syn_pattern(rest, ppp->pp_synp);
5135 reg_do_extmatch = 0;
5136 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005137 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005138 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5139 ppp->pp_matchgroup_id = matchgroup_id;
5140 ++pat_count;
5141 }
5142 }
5143 vim_free(key);
5144 if (illegal || not_enough)
5145 rest = NULL;
5146
5147 /*
5148 * Must have a "start" and "end" pattern.
5149 */
5150 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5151 pat_ptrs[ITEM_END] == NULL))
5152 {
5153 not_enough = TRUE;
5154 rest = NULL;
5155 }
5156
5157 if (rest != NULL)
5158 {
5159 /*
5160 * Check for trailing garbage or command.
5161 * If OK, add the item.
5162 */
5163 eap->nextcmd = check_nextcmd(rest);
5164 if (!ends_excmd(*rest) || eap->skip)
5165 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005166 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005167 && (syn_id = syn_check_group(arg,
5168 (int)(group_name_end - arg))) != 0)
5169 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005170 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005171 /*
5172 * Store the start/skip/end in the syn_items list
5173 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005174 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005175 for (item = ITEM_START; item <= ITEM_END; ++item)
5176 {
5177 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5178 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005179 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5180 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5181 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005182 (item == ITEM_START) ? SPTYPE_START :
5183 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005184 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5185 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005186 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5187 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005188 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005189 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005190#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005191 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005192#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005193 if (item == ITEM_START)
5194 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005195 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005196 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005197 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005198 syn_opt_arg.cont_in_list;
5199 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005200 curwin->w_s->b_syn_containedin = TRUE;
5201 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005202 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005203 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005204 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005205 ++idx;
5206#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005207 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005208 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005209#endif
5210 }
5211 }
5212
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005213 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005214 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005215 success = TRUE; /* don't free the progs and patterns now */
5216 }
5217 }
5218
5219 /*
5220 * Free the allocated memory.
5221 */
5222 for (item = ITEM_START; item <= ITEM_END; ++item)
5223 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5224 {
5225 if (!success)
5226 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005227 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005228 vim_free(ppp->pp_synp->sp_pattern);
5229 }
5230 vim_free(ppp->pp_synp);
5231 ppp_next = ppp->pp_next;
5232 vim_free(ppp);
5233 }
5234
5235 if (!success)
5236 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005237 vim_free(syn_opt_arg.cont_list);
5238 vim_free(syn_opt_arg.cont_in_list);
5239 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005240 if (not_enough)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005241 semsg(_("E399: Not enough arguments: syntax region %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005242 else if (illegal || rest == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005243 semsg(_(e_invarg2), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005244 }
5245}
5246
5247/*
5248 * A simple syntax group ID comparison function suitable for use in qsort()
5249 */
5250 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005251syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005252{
5253 const short *s1 = v1;
5254 const short *s2 = v2;
5255
5256 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5257}
5258
5259/*
5260 * Combines lists of syntax clusters.
5261 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5262 */
5263 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005264syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005265{
5266 int count1 = 0;
5267 int count2 = 0;
5268 short *g1;
5269 short *g2;
5270 short *clstr = NULL;
5271 int count;
5272 int round;
5273
5274 /*
5275 * Handle degenerate cases.
5276 */
5277 if (*clstr2 == NULL)
5278 return;
5279 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5280 {
5281 if (list_op == CLUSTER_REPLACE)
5282 vim_free(*clstr1);
5283 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5284 *clstr1 = *clstr2;
5285 else
5286 vim_free(*clstr2);
5287 return;
5288 }
5289
5290 for (g1 = *clstr1; *g1; g1++)
5291 ++count1;
5292 for (g2 = *clstr2; *g2; g2++)
5293 ++count2;
5294
5295 /*
5296 * For speed purposes, sort both lists.
5297 */
5298 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5299 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5300
5301 /*
5302 * We proceed in two passes; in round 1, we count the elements to place
5303 * in the new list, and in round 2, we allocate and populate the new
5304 * list. For speed, we use a mergesort-like method, adding the smaller
5305 * of the current elements in each list to the new list.
5306 */
5307 for (round = 1; round <= 2; round++)
5308 {
5309 g1 = *clstr1;
5310 g2 = *clstr2;
5311 count = 0;
5312
5313 /*
5314 * First, loop through the lists until one of them is empty.
5315 */
5316 while (*g1 && *g2)
5317 {
5318 /*
5319 * We always want to add from the first list.
5320 */
5321 if (*g1 < *g2)
5322 {
5323 if (round == 2)
5324 clstr[count] = *g1;
5325 count++;
5326 g1++;
5327 continue;
5328 }
5329 /*
5330 * We only want to add from the second list if we're adding the
5331 * lists.
5332 */
5333 if (list_op == CLUSTER_ADD)
5334 {
5335 if (round == 2)
5336 clstr[count] = *g2;
5337 count++;
5338 }
5339 if (*g1 == *g2)
5340 g1++;
5341 g2++;
5342 }
5343
5344 /*
5345 * Now add the leftovers from whichever list didn't get finished
5346 * first. As before, we only want to add from the second list if
5347 * we're adding the lists.
5348 */
5349 for (; *g1; g1++, count++)
5350 if (round == 2)
5351 clstr[count] = *g1;
5352 if (list_op == CLUSTER_ADD)
5353 for (; *g2; g2++, count++)
5354 if (round == 2)
5355 clstr[count] = *g2;
5356
5357 if (round == 1)
5358 {
5359 /*
5360 * If the group ended up empty, we don't need to allocate any
5361 * space for it.
5362 */
5363 if (count == 0)
5364 {
5365 clstr = NULL;
5366 break;
5367 }
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005368 clstr = ALLOC_MULT(short, count + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005369 if (clstr == NULL)
5370 break;
5371 clstr[count] = 0;
5372 }
5373 }
5374
5375 /*
5376 * Finally, put the new list in place.
5377 */
5378 vim_free(*clstr1);
5379 vim_free(*clstr2);
5380 *clstr1 = clstr;
5381}
5382
5383/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005384 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005385 * If it is not found, 0 is returned.
5386 */
5387 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005388syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005389{
5390 int i;
5391 char_u *name_u;
5392
5393 /* Avoid using stricmp() too much, it's slow on some systems */
5394 name_u = vim_strsave_up(name);
5395 if (name_u == NULL)
5396 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005397 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5398 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5399 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005400 break;
5401 vim_free(name_u);
5402 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5403}
5404
5405/*
5406 * Like syn_scl_name2id(), but take a pointer + length argument.
5407 */
5408 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005409syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005410{
5411 char_u *name;
5412 int id = 0;
5413
5414 name = vim_strnsave(linep, len);
5415 if (name != NULL)
5416 {
5417 id = syn_scl_name2id(name);
5418 vim_free(name);
5419 }
5420 return id;
5421}
5422
5423/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005424 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005425 * The argument is a pointer to the name and the length of the name.
5426 * If it doesn't exist yet, a new entry is created.
5427 * Return 0 for failure.
5428 */
5429 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005430syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005431{
5432 int id;
5433 char_u *name;
5434
5435 name = vim_strnsave(pp, len);
5436 if (name == NULL)
5437 return 0;
5438
5439 id = syn_scl_name2id(name);
5440 if (id == 0) /* doesn't exist yet */
5441 id = syn_add_cluster(name);
5442 else
5443 vim_free(name);
5444 return id;
5445}
5446
5447/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005448 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005449 * "name" must be an allocated string, it will be consumed.
5450 * Return 0 for failure.
5451 */
5452 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005453syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005454{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005455 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005456
5457 /*
5458 * First call for this growarray: init growing array.
5459 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005460 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005461 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005462 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5463 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005464 }
5465
Bram Moolenaar42431a72011-04-01 14:44:59 +02005466 len = curwin->w_s->b_syn_clusters.ga_len;
5467 if (len >= MAX_CLUSTER_ID)
5468 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005469 emsg(_("E848: Too many syntax clusters"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02005470 vim_free(name);
5471 return 0;
5472 }
5473
Bram Moolenaar071d4272004-06-13 20:20:40 +00005474 /*
5475 * Make room for at least one other cluster entry.
5476 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005477 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005478 {
5479 vim_free(name);
5480 return 0;
5481 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005482
Bram Moolenaar860cae12010-06-05 23:22:07 +02005483 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5484 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5485 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5486 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5487 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005488
Bram Moolenaar217ad922005-03-20 22:37:15 +00005489 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005490 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005491 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005492 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005493
Bram Moolenaar071d4272004-06-13 20:20:40 +00005494 return len + SYNID_CLUSTER;
5495}
5496
5497/*
5498 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5499 * [add={groupname},..] [remove={groupname},..]".
5500 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005501 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005502syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005503{
5504 char_u *arg = eap->arg;
5505 char_u *group_name_end;
5506 char_u *rest;
5507 int scl_id;
5508 short *clstr_list;
5509 int got_clstr = FALSE;
5510 int opt_len;
5511 int list_op;
5512
5513 eap->nextcmd = find_nextcmd(arg);
5514 if (eap->skip)
5515 return;
5516
5517 rest = get_group_name(arg, &group_name_end);
5518
5519 if (rest != NULL)
5520 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005521 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5522 if (scl_id == 0)
5523 return;
5524 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005525
5526 for (;;)
5527 {
5528 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005529 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005530 {
5531 opt_len = 3;
5532 list_op = CLUSTER_ADD;
5533 }
5534 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005535 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005536 {
5537 opt_len = 6;
5538 list_op = CLUSTER_SUBTRACT;
5539 }
5540 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005541 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005542 {
5543 opt_len = 8;
5544 list_op = CLUSTER_REPLACE;
5545 }
5546 else
5547 break;
5548
5549 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005550 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005551 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005552 semsg(_(e_invarg2), rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005553 break;
5554 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005555 if (scl_id >= 0)
5556 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005557 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005558 else
5559 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005560 got_clstr = TRUE;
5561 }
5562
5563 if (got_clstr)
5564 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005565 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005566 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005567 }
5568 }
5569
5570 if (!got_clstr)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005571 emsg(_("E400: No cluster specified"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005572 if (rest == NULL || !ends_excmd(*rest))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005573 semsg(_(e_invarg2), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005574}
5575
5576/*
5577 * On first call for current buffer: Init growing array.
5578 */
5579 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005580init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005581{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005582 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5583 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005584}
5585
5586/*
5587 * Get one pattern for a ":syntax match" or ":syntax region" command.
5588 * Stores the pattern and program in a synpat_T.
5589 * Returns a pointer to the next argument, or NULL in case of an error.
5590 */
5591 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005592get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005593{
5594 char_u *end;
5595 int *p;
5596 int idx;
5597 char_u *cpo_save;
5598
5599 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005600 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005601 return NULL;
5602
5603 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5604 if (*end != *arg) /* end delimiter not found */
5605 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005606 semsg(_("E401: Pattern delimiter not found: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005607 return NULL;
5608 }
5609 /* store the pattern and compiled regexp program */
5610 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5611 return NULL;
5612
5613 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5614 cpo_save = p_cpo;
5615 p_cpo = (char_u *)"";
5616 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5617 p_cpo = cpo_save;
5618
5619 if (ci->sp_prog == NULL)
5620 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005621 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005622#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005623 syn_clear_time(&ci->sp_time);
5624#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005625
5626 /*
5627 * Check for a match, highlight or region offset.
5628 */
5629 ++end;
5630 do
5631 {
5632 for (idx = SPO_COUNT; --idx >= 0; )
5633 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5634 break;
5635 if (idx >= 0)
5636 {
5637 p = &(ci->sp_offsets[idx]);
5638 if (idx != SPO_LC_OFF)
5639 switch (end[3])
5640 {
5641 case 's': break;
5642 case 'b': break;
5643 case 'e': idx += SPO_COUNT; break;
5644 default: idx = -1; break;
5645 }
5646 if (idx >= 0)
5647 {
5648 ci->sp_off_flags |= (1 << idx);
5649 if (idx == SPO_LC_OFF) /* lc=99 */
5650 {
5651 end += 3;
5652 *p = getdigits(&end);
5653
5654 /* "lc=" offset automatically sets "ms=" offset */
5655 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5656 {
5657 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5658 ci->sp_offsets[SPO_MS_OFF] = *p;
5659 }
5660 }
5661 else /* yy=x+99 */
5662 {
5663 end += 4;
5664 if (*end == '+')
5665 {
5666 ++end;
5667 *p = getdigits(&end); /* positive offset */
5668 }
5669 else if (*end == '-')
5670 {
5671 ++end;
5672 *p = -getdigits(&end); /* negative offset */
5673 }
5674 }
5675 if (*end != ',')
5676 break;
5677 ++end;
5678 }
5679 }
5680 } while (idx >= 0);
5681
Bram Moolenaar1c465442017-03-12 20:10:05 +01005682 if (!ends_excmd(*end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005683 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005684 semsg(_("E402: Garbage after pattern: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005685 return NULL;
5686 }
5687 return skipwhite(end);
5688}
5689
5690/*
5691 * Handle ":syntax sync .." command.
5692 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005693 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005694syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005695{
5696 char_u *arg_start = eap->arg;
5697 char_u *arg_end;
5698 char_u *key = NULL;
5699 char_u *next_arg;
5700 int illegal = FALSE;
5701 int finished = FALSE;
5702 long n;
5703 char_u *cpo_save;
5704
5705 if (ends_excmd(*arg_start))
5706 {
5707 syn_cmd_list(eap, TRUE);
5708 return;
5709 }
5710
5711 while (!ends_excmd(*arg_start))
5712 {
5713 arg_end = skiptowhite(arg_start);
5714 next_arg = skipwhite(arg_end);
5715 vim_free(key);
5716 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5717 if (STRCMP(key, "CCOMMENT") == 0)
5718 {
5719 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005720 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005721 if (!ends_excmd(*next_arg))
5722 {
5723 arg_end = skiptowhite(next_arg);
5724 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005725 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005726 (int)(arg_end - next_arg));
5727 next_arg = skipwhite(arg_end);
5728 }
5729 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005730 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005731 }
5732 else if ( STRNCMP(key, "LINES", 5) == 0
5733 || STRNCMP(key, "MINLINES", 8) == 0
5734 || STRNCMP(key, "MAXLINES", 8) == 0
5735 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5736 {
5737 if (key[4] == 'S')
5738 arg_end = key + 6;
5739 else if (key[0] == 'L')
5740 arg_end = key + 11;
5741 else
5742 arg_end = key + 9;
5743 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5744 {
5745 illegal = TRUE;
5746 break;
5747 }
5748 n = getdigits(&arg_end);
5749 if (!eap->skip)
5750 {
5751 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005752 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005753 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005754 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005755 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005756 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005757 }
5758 }
5759 else if (STRCMP(key, "FROMSTART") == 0)
5760 {
5761 if (!eap->skip)
5762 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005763 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5764 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005765 }
5766 }
5767 else if (STRCMP(key, "LINECONT") == 0)
5768 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005769 if (*next_arg == NUL) /* missing pattern */
5770 {
5771 illegal = TRUE;
5772 break;
5773 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005774 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005775 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005776 emsg(_("E403: syntax sync: line continuations pattern specified twice"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005777 finished = TRUE;
5778 break;
5779 }
5780 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5781 if (*arg_end != *next_arg) /* end delimiter not found */
5782 {
5783 illegal = TRUE;
5784 break;
5785 }
5786
5787 if (!eap->skip)
5788 {
5789 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005790 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005791 (int)(arg_end - next_arg - 1))) == NULL)
5792 {
5793 finished = TRUE;
5794 break;
5795 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005796 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005797
5798 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5799 cpo_save = p_cpo;
5800 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005801 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005802 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005803 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005804#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005805 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5806#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005807
Bram Moolenaar860cae12010-06-05 23:22:07 +02005808 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005809 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01005810 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005811 finished = TRUE;
5812 break;
5813 }
5814 }
5815 next_arg = skipwhite(arg_end + 1);
5816 }
5817 else
5818 {
5819 eap->arg = next_arg;
5820 if (STRCMP(key, "MATCH") == 0)
5821 syn_cmd_match(eap, TRUE);
5822 else if (STRCMP(key, "REGION") == 0)
5823 syn_cmd_region(eap, TRUE);
5824 else if (STRCMP(key, "CLEAR") == 0)
5825 syn_cmd_clear(eap, TRUE);
5826 else
5827 illegal = TRUE;
5828 finished = TRUE;
5829 break;
5830 }
5831 arg_start = next_arg;
5832 }
5833 vim_free(key);
5834 if (illegal)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005835 semsg(_("E404: Illegal arguments: %s"), arg_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005836 else if (!finished)
5837 {
5838 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005839 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005840 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005841 }
5842}
5843
5844/*
5845 * Convert a line of highlight group names into a list of group ID numbers.
5846 * "arg" should point to the "contains" or "nextgroup" keyword.
5847 * "arg" is advanced to after the last group name.
5848 * Careful: the argument is modified (NULs added).
5849 * returns FAIL for some error, OK for success.
5850 */
5851 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005852get_id_list(
5853 char_u **arg,
5854 int keylen, /* length of keyword */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005855 short **list, /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00005856 NULL, the list is silently skipped! */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005857 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005858{
5859 char_u *p = NULL;
5860 char_u *end;
5861 int round;
5862 int count;
5863 int total_count = 0;
5864 short *retval = NULL;
5865 char_u *name;
5866 regmatch_T regmatch;
5867 int id;
5868 int i;
5869 int failed = FALSE;
5870
5871 /*
5872 * We parse the list twice:
5873 * round == 1: count the number of items, allocate the array.
5874 * round == 2: fill the array with the items.
5875 * In round 1 new groups may be added, causing the number of items to
5876 * grow when a regexp is used. In that case round 1 is done once again.
5877 */
5878 for (round = 1; round <= 2; ++round)
5879 {
5880 /*
5881 * skip "contains"
5882 */
5883 p = skipwhite(*arg + keylen);
5884 if (*p != '=')
5885 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005886 semsg(_("E405: Missing equal sign: %s"), *arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005887 break;
5888 }
5889 p = skipwhite(p + 1);
5890 if (ends_excmd(*p))
5891 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005892 semsg(_("E406: Empty argument: %s"), *arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005893 break;
5894 }
5895
5896 /*
5897 * parse the arguments after "contains"
5898 */
5899 count = 0;
5900 while (!ends_excmd(*p))
5901 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01005902 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005903 ;
Bram Moolenaar51e14382019-05-25 20:21:28 +02005904 name = alloc(end - p + 3); /* leave room for "^$" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005905 if (name == NULL)
5906 {
5907 failed = TRUE;
5908 break;
5909 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005910 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005911 if ( STRCMP(name + 1, "ALLBUT") == 0
5912 || STRCMP(name + 1, "ALL") == 0
5913 || STRCMP(name + 1, "TOP") == 0
5914 || STRCMP(name + 1, "CONTAINED") == 0)
5915 {
5916 if (TOUPPER_ASC(**arg) != 'C')
5917 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005918 semsg(_("E407: %s not allowed here"), name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005919 failed = TRUE;
5920 vim_free(name);
5921 break;
5922 }
5923 if (count != 0)
5924 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005925 semsg(_("E408: %s must be first in contains list"),
Bram Moolenaard7a96152017-01-22 15:28:55 +01005926 name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005927 failed = TRUE;
5928 vim_free(name);
5929 break;
5930 }
5931 if (name[1] == 'A')
5932 id = SYNID_ALLBUT;
5933 else if (name[1] == 'T')
5934 id = SYNID_TOP;
5935 else
5936 id = SYNID_CONTAINED;
5937 id += current_syn_inc_tag;
5938 }
5939 else if (name[1] == '@')
5940 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01005941 if (skip)
5942 id = -1;
5943 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01005944 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005945 }
5946 else
5947 {
5948 /*
5949 * Handle full group name.
5950 */
5951 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5952 id = syn_check_group(name + 1, (int)(end - p));
5953 else
5954 {
5955 /*
5956 * Handle match of regexp with group names.
5957 */
5958 *name = '^';
5959 STRCAT(name, "$");
5960 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5961 if (regmatch.regprog == NULL)
5962 {
5963 failed = TRUE;
5964 vim_free(name);
5965 break;
5966 }
5967
5968 regmatch.rm_ic = TRUE;
5969 id = 0;
5970 for (i = highlight_ga.ga_len; --i >= 0; )
5971 {
5972 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5973 (colnr_T)0))
5974 {
5975 if (round == 2)
5976 {
5977 /* Got more items than expected; can happen
5978 * when adding items that match:
5979 * "contains=a.*b,axb".
5980 * Go back to first round */
5981 if (count >= total_count)
5982 {
5983 vim_free(retval);
5984 round = 1;
5985 }
5986 else
5987 retval[count] = i + 1;
5988 }
5989 ++count;
5990 id = -1; /* remember that we found one */
5991 }
5992 }
Bram Moolenaar473de612013-06-08 18:19:48 +02005993 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005994 }
5995 }
5996 vim_free(name);
5997 if (id == 0)
5998 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005999 semsg(_("E409: Unknown group name: %s"), p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006000 failed = TRUE;
6001 break;
6002 }
6003 if (id > 0)
6004 {
6005 if (round == 2)
6006 {
6007 /* Got more items than expected, go back to first round */
6008 if (count >= total_count)
6009 {
6010 vim_free(retval);
6011 round = 1;
6012 }
6013 else
6014 retval[count] = id;
6015 }
6016 ++count;
6017 }
6018 p = skipwhite(end);
6019 if (*p != ',')
6020 break;
6021 p = skipwhite(p + 1); /* skip comma in between arguments */
6022 }
6023 if (failed)
6024 break;
6025 if (round == 1)
6026 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006027 retval = ALLOC_MULT(short, count + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006028 if (retval == NULL)
6029 break;
6030 retval[count] = 0; /* zero means end of the list */
6031 total_count = count;
6032 }
6033 }
6034
6035 *arg = p;
6036 if (failed || retval == NULL)
6037 {
6038 vim_free(retval);
6039 return FAIL;
6040 }
6041
6042 if (*list == NULL)
6043 *list = retval;
6044 else
6045 vim_free(retval); /* list already found, don't overwrite it */
6046
6047 return OK;
6048}
6049
6050/*
6051 * Make a copy of an ID list.
6052 */
6053 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006054copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006055{
6056 int len;
6057 int count;
6058 short *retval;
6059
6060 if (list == NULL)
6061 return NULL;
6062
6063 for (count = 0; list[count]; ++count)
6064 ;
6065 len = (count + 1) * sizeof(short);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006066 retval = alloc(len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006067 if (retval != NULL)
6068 mch_memmove(retval, list, (size_t)len);
6069
6070 return retval;
6071}
6072
6073/*
6074 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6075 * "cur_si" can be NULL if not checking the "containedin" list.
6076 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6077 * the current item.
6078 * This function is called very often, keep it fast!!
6079 */
6080 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006081in_id_list(
6082 stateitem_T *cur_si, /* current item or NULL */
6083 short *list, /* id list */
6084 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6085 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006086{
6087 int retval;
6088 short *scl_list;
6089 short item;
6090 short id = ssp->id;
6091 static int depth = 0;
6092 int r;
6093
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006094 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006095 if (cur_si != NULL && ssp->cont_in_list != NULL
6096 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006097 {
6098 /* Ignore transparent items without a contains argument. Double check
6099 * that we don't go back past the first one. */
6100 while ((cur_si->si_flags & HL_TRANS_CONT)
6101 && cur_si > (stateitem_T *)(current_state.ga_data))
6102 --cur_si;
6103 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6104 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006105 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6106 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006107 return TRUE;
6108 }
6109
6110 if (list == NULL)
6111 return FALSE;
6112
6113 /*
6114 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6115 * inside anything. Only allow not-contained groups.
6116 */
6117 if (list == ID_LIST_ALL)
6118 return !contained;
6119
6120 /*
6121 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6122 * contains list. We also require that "id" is at the same ":syn include"
6123 * level as the list.
6124 */
6125 item = *list;
6126 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6127 {
6128 if (item < SYNID_TOP)
6129 {
6130 /* ALL or ALLBUT: accept all groups in the same file */
6131 if (item - SYNID_ALLBUT != ssp->inc_tag)
6132 return FALSE;
6133 }
6134 else if (item < SYNID_CONTAINED)
6135 {
6136 /* TOP: accept all not-contained groups in the same file */
6137 if (item - SYNID_TOP != ssp->inc_tag || contained)
6138 return FALSE;
6139 }
6140 else
6141 {
6142 /* CONTAINED: accept all contained groups in the same file */
6143 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6144 return FALSE;
6145 }
6146 item = *++list;
6147 retval = FALSE;
6148 }
6149 else
6150 retval = TRUE;
6151
6152 /*
6153 * Return "retval" if id is in the contains list.
6154 */
6155 while (item != 0)
6156 {
6157 if (item == id)
6158 return retval;
6159 if (item >= SYNID_CLUSTER)
6160 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006161 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006162 /* restrict recursiveness to 30 to avoid an endless loop for a
6163 * cluster that includes itself (indirectly) */
6164 if (scl_list != NULL && depth < 30)
6165 {
6166 ++depth;
6167 r = in_id_list(NULL, scl_list, ssp, contained);
6168 --depth;
6169 if (r)
6170 return retval;
6171 }
6172 }
6173 item = *++list;
6174 }
6175 return !retval;
6176}
6177
6178struct subcommand
6179{
Bram Moolenaard99df422016-01-29 23:20:40 +01006180 char *name; /* subcommand name */
6181 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006182};
6183
6184static struct subcommand subcommands[] =
6185{
6186 {"case", syn_cmd_case},
6187 {"clear", syn_cmd_clear},
6188 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006189 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006190 {"enable", syn_cmd_enable},
6191 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006192 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006193 {"keyword", syn_cmd_keyword},
6194 {"list", syn_cmd_list},
6195 {"manual", syn_cmd_manual},
6196 {"match", syn_cmd_match},
6197 {"on", syn_cmd_on},
6198 {"off", syn_cmd_off},
6199 {"region", syn_cmd_region},
6200 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006201 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006202 {"sync", syn_cmd_sync},
6203 {"", syn_cmd_list},
6204 {NULL, NULL}
6205};
6206
6207/*
6208 * ":syntax".
6209 * This searches the subcommands[] table for the subcommand name, and calls a
6210 * syntax_subcommand() function to do the rest.
6211 */
6212 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006213ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006214{
6215 char_u *arg = eap->arg;
6216 char_u *subcmd_end;
6217 char_u *subcmd_name;
6218 int i;
6219
6220 syn_cmdlinep = eap->cmdlinep;
6221
6222 /* isolate subcommand name */
6223 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6224 ;
6225 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6226 if (subcmd_name != NULL)
6227 {
6228 if (eap->skip) /* skip error messages for all subcommands */
6229 ++emsg_skip;
6230 for (i = 0; ; ++i)
6231 {
6232 if (subcommands[i].name == NULL)
6233 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006234 semsg(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006235 break;
6236 }
6237 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6238 {
6239 eap->arg = skipwhite(subcmd_end);
6240 (subcommands[i].func)(eap, FALSE);
6241 break;
6242 }
6243 }
6244 vim_free(subcmd_name);
6245 if (eap->skip)
6246 --emsg_skip;
6247 }
6248}
6249
Bram Moolenaar860cae12010-06-05 23:22:07 +02006250 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006251ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006252{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006253 char_u *old_value;
6254 char_u *new_value;
6255
Bram Moolenaar860cae12010-06-05 23:22:07 +02006256 if (curwin->w_s == &curwin->w_buffer->b_s)
6257 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006258 curwin->w_s = ALLOC_ONE(synblock_T);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006259 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006260 hash_init(&curwin->w_s->b_keywtab);
6261 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006262#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006263 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006264 curwin->w_p_spell = FALSE; /* No spell checking */
6265 clear_string_option(&curwin->w_s->b_p_spc);
6266 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006267 clear_string_option(&curwin->w_s->b_p_spl);
6268#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006269 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006270 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006271
6272 /* save value of b:current_syntax */
6273 old_value = get_var_value((char_u *)"b:current_syntax");
6274 if (old_value != NULL)
6275 old_value = vim_strsave(old_value);
6276
6277 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6278 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006279 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006280
6281 /* move value of b:current_syntax to w:current_syntax */
6282 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006283 if (new_value != NULL)
6284 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006285
6286 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006287 if (old_value == NULL)
6288 do_unlet((char_u *)"b:current_syntax", TRUE);
6289 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006290 {
6291 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6292 vim_free(old_value);
6293 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006294}
6295
6296 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006297syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006298{
6299 return (win->w_s->b_syn_patterns.ga_len != 0
6300 || win->w_s->b_syn_clusters.ga_len != 0
6301 || win->w_s->b_keywtab.ht_used > 0
6302 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006303}
6304
6305#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6306
6307static enum
6308{
6309 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006310 EXP_CASE, /* expand ":syn case" arguments */
6311 EXP_SPELL, /* expand ":syn spell" arguments */
6312 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006313} expand_what;
6314
Bram Moolenaar4f688582007-07-24 12:34:30 +00006315/*
6316 * Reset include_link, include_default, include_none to 0.
6317 * Called when we are done expanding.
6318 */
6319 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006320reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006321{
6322 include_link = include_default = include_none = 0;
6323}
6324
6325/*
6326 * Handle command line completion for :match and :echohl command: Add "None"
6327 * as highlight group.
6328 */
6329 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006330set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006331{
6332 xp->xp_context = EXPAND_HIGHLIGHT;
6333 xp->xp_pattern = arg;
6334 include_none = 1;
6335}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006336
6337/*
6338 * Handle command line completion for :syntax command.
6339 */
6340 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006341set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006342{
6343 char_u *p;
6344
6345 /* Default: expand subcommands */
6346 xp->xp_context = EXPAND_SYNTAX;
6347 expand_what = EXP_SUBCMD;
6348 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006349 include_link = 0;
6350 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006351
6352 /* (part of) subcommand already typed */
6353 if (*arg != NUL)
6354 {
6355 p = skiptowhite(arg);
6356 if (*p != NUL) /* past first word */
6357 {
6358 xp->xp_pattern = skipwhite(p);
6359 if (*skiptowhite(xp->xp_pattern) != NUL)
6360 xp->xp_context = EXPAND_NOTHING;
6361 else if (STRNICMP(arg, "case", p - arg) == 0)
6362 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006363 else if (STRNICMP(arg, "spell", p - arg) == 0)
6364 expand_what = EXP_SPELL;
6365 else if (STRNICMP(arg, "sync", p - arg) == 0)
6366 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006367 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6368 || STRNICMP(arg, "region", p - arg) == 0
6369 || STRNICMP(arg, "match", p - arg) == 0
6370 || STRNICMP(arg, "list", p - arg) == 0)
6371 xp->xp_context = EXPAND_HIGHLIGHT;
6372 else
6373 xp->xp_context = EXPAND_NOTHING;
6374 }
6375 }
6376}
6377
Bram Moolenaar071d4272004-06-13 20:20:40 +00006378/*
6379 * Function given to ExpandGeneric() to obtain the list syntax names for
6380 * expansion.
6381 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006382 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006383get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006384{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006385 switch (expand_what)
6386 {
6387 case EXP_SUBCMD:
6388 return (char_u *)subcommands[idx].name;
6389 case EXP_CASE:
6390 {
6391 static char *case_args[] = {"match", "ignore", NULL};
6392 return (char_u *)case_args[idx];
6393 }
6394 case EXP_SPELL:
6395 {
6396 static char *spell_args[] =
6397 {"toplevel", "notoplevel", "default", NULL};
6398 return (char_u *)spell_args[idx];
6399 }
6400 case EXP_SYNC:
6401 {
6402 static char *sync_args[] =
6403 {"ccomment", "clear", "fromstart",
6404 "linebreaks=", "linecont", "lines=", "match",
6405 "maxlines=", "minlines=", "region", NULL};
6406 return (char_u *)sync_args[idx];
6407 }
6408 }
6409 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006410}
6411
6412#endif /* FEAT_CMDL_COMPL */
6413
Bram Moolenaar071d4272004-06-13 20:20:40 +00006414/*
6415 * Function called for expression evaluation: get syntax ID at file position.
6416 */
6417 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006418syn_get_id(
6419 win_T *wp,
6420 long lnum,
6421 colnr_T col,
6422 int trans, /* remove transparency */
6423 int *spellp, /* return: can do spell checking */
6424 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006425{
6426 /* When the position is not after the current position and in the same
6427 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006428 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006429 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006430 || col < current_col)
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006431 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006432 else if (wp->w_buffer == syn_buf
6433 && lnum == current_lnum
6434 && col > current_col)
6435 /* next_match may not be correct when moving around, e.g. with the
6436 * "skip" expression in searchpair() */
6437 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006438
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006439 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006440
6441 return (trans ? current_trans_id : current_id);
6442}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006443
Bram Moolenaar860cae12010-06-05 23:22:07 +02006444#if defined(FEAT_CONCEAL) || defined(PROTO)
6445/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006446 * Get extra information about the syntax item. Must be called right after
6447 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006448 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006449 * Returns the current flags.
6450 */
6451 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006452get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006453{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006454 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006455 return current_flags;
6456}
6457
6458/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006459 * Return conceal substitution character
6460 */
6461 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006462syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006463{
6464 return current_sub_char;
6465}
6466#endif
6467
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006468#if defined(FEAT_EVAL) || defined(PROTO)
6469/*
6470 * Return the syntax ID at position "i" in the current stack.
6471 * The caller must have called syn_get_id() before to fill the stack.
6472 * Returns -1 when "i" is out of range.
6473 */
6474 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006475syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006476{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006477 if (i >= current_state.ga_len)
6478 {
6479 /* Need to invalidate the state, because we didn't properly finish it
6480 * for the last character, "keep_state" was TRUE. */
6481 invalidate_current_state();
6482 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006483 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006484 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006485 return CUR_STATE(i).si_id;
6486}
6487#endif
6488
Bram Moolenaar071d4272004-06-13 20:20:40 +00006489#if defined(FEAT_FOLDING) || defined(PROTO)
6490/*
6491 * Function called to get folding level for line "lnum" in window "wp".
6492 */
6493 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006494syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006495{
6496 int level = 0;
6497 int i;
6498
6499 /* Return quickly when there are no fold items at all. */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006500 if (wp->w_s->b_syn_folditems != 0
6501 && !wp->w_s->b_syn_error
6502# ifdef SYN_TIME_LIMIT
6503 && !wp->w_s->b_syn_slow
6504# endif
6505 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006506 {
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006507 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006508
6509 for (i = 0; i < current_state.ga_len; ++i)
6510 if (CUR_STATE(i).si_flags & HL_FOLD)
6511 ++level;
6512 }
6513 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006514 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006515 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006516 if (level < 0)
6517 level = 0;
6518 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006519 return level;
6520}
6521#endif
6522
Bram Moolenaar01615492015-02-03 13:00:38 +01006523#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006524/*
6525 * ":syntime".
6526 */
6527 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006528ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006529{
6530 if (STRCMP(eap->arg, "on") == 0)
6531 syn_time_on = TRUE;
6532 else if (STRCMP(eap->arg, "off") == 0)
6533 syn_time_on = FALSE;
6534 else if (STRCMP(eap->arg, "clear") == 0)
6535 syntime_clear();
6536 else if (STRCMP(eap->arg, "report") == 0)
6537 syntime_report();
6538 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006539 semsg(_(e_invarg2), eap->arg);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006540}
6541
6542 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006543syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006544{
6545 profile_zero(&st->total);
6546 profile_zero(&st->slowest);
6547 st->count = 0;
6548 st->match = 0;
6549}
6550
6551/*
6552 * Clear the syntax timing for the current buffer.
6553 */
6554 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006555syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006556{
6557 int idx;
6558 synpat_T *spp;
6559
6560 if (!syntax_present(curwin))
6561 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006562 msg(_(msg_no_items));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006563 return;
6564 }
6565 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6566 {
6567 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6568 syn_clear_time(&spp->sp_time);
6569 }
6570}
6571
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006572#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6573/*
6574 * Function given to ExpandGeneric() to obtain the possible arguments of the
6575 * ":syntime {on,off,clear,report}" command.
6576 */
6577 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006578get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006579{
6580 switch (idx)
6581 {
6582 case 0: return (char_u *)"on";
6583 case 1: return (char_u *)"off";
6584 case 2: return (char_u *)"clear";
6585 case 3: return (char_u *)"report";
6586 }
6587 return NULL;
6588}
6589#endif
6590
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006591typedef struct
6592{
6593 proftime_T total;
6594 int count;
6595 int match;
6596 proftime_T slowest;
6597 proftime_T average;
6598 int id;
6599 char_u *pattern;
6600} time_entry_T;
6601
6602 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006603syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006604{
6605 const time_entry_T *s1 = v1;
6606 const time_entry_T *s2 = v2;
6607
6608 return profile_cmp(&s1->total, &s2->total);
6609}
6610
6611/*
6612 * Clear the syntax timing for the current buffer.
6613 */
6614 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006615syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006616{
6617 int idx;
6618 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006619# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006620 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006621# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006622 int len;
6623 proftime_T total_total;
6624 int total_count = 0;
6625 garray_T ga;
6626 time_entry_T *p;
6627
6628 if (!syntax_present(curwin))
6629 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006630 msg(_(msg_no_items));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006631 return;
6632 }
6633
6634 ga_init2(&ga, sizeof(time_entry_T), 50);
6635 profile_zero(&total_total);
6636 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6637 {
6638 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6639 if (spp->sp_time.count > 0)
6640 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006641 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006642 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6643 p->total = spp->sp_time.total;
6644 profile_add(&total_total, &spp->sp_time.total);
6645 p->count = spp->sp_time.count;
6646 p->match = spp->sp_time.match;
6647 total_count += spp->sp_time.count;
6648 p->slowest = spp->sp_time.slowest;
6649# ifdef FEAT_FLOAT
6650 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6651 p->average = tm;
6652# endif
6653 p->id = spp->sp_syn.id;
6654 p->pattern = spp->sp_pattern;
6655 ++ga.ga_len;
6656 }
6657 }
6658
Bram Moolenaara2162552017-01-08 17:46:20 +01006659 /* Sort on total time. Skip if there are no items to avoid passing NULL
6660 * pointer to qsort(). */
6661 if (ga.ga_len > 1)
6662 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006663 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006664
Bram Moolenaar32526b32019-01-19 17:43:09 +01006665 msg_puts_title(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6666 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006667 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6668 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006669 p = ((time_entry_T *)ga.ga_data) + idx;
6670
Bram Moolenaar32526b32019-01-19 17:43:09 +01006671 msg_puts(profile_msg(&p->total));
6672 msg_puts(" "); /* make sure there is always a separating space */
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006673 msg_advance(13);
6674 msg_outnum(p->count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006675 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006676 msg_advance(20);
6677 msg_outnum(p->match);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006678 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006679 msg_advance(26);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006680 msg_puts(profile_msg(&p->slowest));
6681 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006682 msg_advance(38);
6683# ifdef FEAT_FLOAT
Bram Moolenaar32526b32019-01-19 17:43:09 +01006684 msg_puts(profile_msg(&p->average));
6685 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006686# endif
6687 msg_advance(50);
6688 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006689 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006690
6691 msg_advance(69);
6692 if (Columns < 80)
6693 len = 20; /* will wrap anyway */
6694 else
6695 len = Columns - 70;
6696 if (len > (int)STRLEN(p->pattern))
6697 len = (int)STRLEN(p->pattern);
6698 msg_outtrans_len(p->pattern, len);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006699 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006700 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006701 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006702 if (!got_int)
6703 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006704 msg_puts("\n");
6705 msg_puts(profile_msg(&total_total));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006706 msg_advance(13);
6707 msg_outnum(total_count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006708 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006709 }
6710}
6711#endif
6712
Bram Moolenaar071d4272004-06-13 20:20:40 +00006713#endif /* FEAT_SYN_HL */