blob: 6b71e8dfabeefda1bdea4a1234aaa3ce9373f288 [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
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010018#define SYN_NAMELEN 50 // maximum length of a syntax name
Bram Moolenaar071d4272004-06-13 20:20:40 +000019
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010020// 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
Bram Moolenaar071d4272004-06-13 20:20:40 +000028#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{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010048 char sp_type; // see SPTYPE_ defines below
49 char sp_syncing; // this item used for syncing
50 short sp_syn_match_id; // highlight group ID of pattern
51 short sp_off_flags; // see below
52 int sp_offsets[SPO_COUNT]; // offsets
53 int sp_flags; // see HL_ defines below
Bram Moolenaar860cae12010-06-05 23:22:07 +020054#ifdef FEAT_CONCEAL
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010055 int sp_cchar; // conceal substitute character
Bram Moolenaar860cae12010-06-05 23:22:07 +020056#endif
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +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
63 struct sp_syn sp_syn; // struct passed to in_id_list()
64 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
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010071// 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.
Bram Moolenaar071d4272004-06-13 20:20:40 +000075
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010076#define SPTYPE_MATCH 1 // match keyword with this group ID
77#define SPTYPE_START 2 // match a regexp, start of item
78#define SPTYPE_END 3 // match a regexp, end of item
79#define SPTYPE_SKIP 4 // match a regexp, skip within item
Bram Moolenaar071d4272004-06-13 20:20:40 +000080
Bram Moolenaar071d4272004-06-13 20:20:40 +000081
82#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
83
kylo252ae6f1d82022-02-16 19:24:07 +000084#define NONE_IDX (-2) // value of sp_sync_idx for "NONE"
Bram Moolenaar071d4272004-06-13 20:20:40 +000085
86/*
87 * Flags for b_syn_sync_flags:
88 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010089#define SF_CCOMMENT 0x01 // sync on a C-style comment
90#define SF_MATCH 0x02 // sync by matching a pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +000091
92#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
93
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010094#define MAXKEYWLEN 80 // maximum length of a keyword
Bram Moolenaar071d4272004-06-13 20:20:40 +000095
96/*
97 * The attributes of the syntax item that has been recognized.
98 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010099static int current_attr = 0; // attr of current syntax word
Bram Moolenaar071d4272004-06-13 20:20:40 +0000100#ifdef FEAT_EVAL
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100101static int current_id = 0; // ID of current char for syn_get_id()
102static int current_trans_id = 0; // idem, transparency removed
Bram Moolenaar071d4272004-06-13 20:20:40 +0000103#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200104#ifdef FEAT_CONCEAL
105static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200106static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200107static int current_sub_char = 0;
108#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000109
Bram Moolenaar217ad922005-03-20 22:37:15 +0000110typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000111{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100112 char_u *scl_name; // syntax cluster name
113 char_u *scl_name_u; // uppercase of scl_name
114 short *scl_list; // IDs in this syntax cluster
Bram Moolenaar217ad922005-03-20 22:37:15 +0000115} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000116
117/*
118 * Methods of combining two clusters
119 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100120#define CLUSTER_REPLACE 1 // replace first list with second
121#define CLUSTER_ADD 2 // add second list to first
122#define CLUSTER_SUBTRACT 3 // subtract second list from first
Bram Moolenaar071d4272004-06-13 20:20:40 +0000123
Bram Moolenaar217ad922005-03-20 22:37:15 +0000124#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000125
126/*
127 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200128 * 0 - 19999 normal syntax groups
129 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
130 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
131 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
132 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000133 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100134#define SYNID_ALLBUT MAX_HL_ID // syntax group ID for contains=ALLBUT
135#define SYNID_TOP 21000 // syntax group ID for contains=TOP
136#define SYNID_CONTAINED 22000 // syntax group ID for contains=CONTAINED
137#define SYNID_CLUSTER 23000 // first syntax group ID for clusters
Bram Moolenaar42431a72011-04-01 14:44:59 +0200138
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100139#define MAX_SYN_INC_TAG 999 // maximum before the above overflow
Bram Moolenaar42431a72011-04-01 14:44:59 +0200140#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000141
142/*
143 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
144 * expand_filename(). Most of the other syntax commands don't need it, so
145 * instead of passing it to them, we stow it here.
146 */
147static char_u **syn_cmdlinep;
148
149/*
150 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200151 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000152 * rules in each ":syn include"'d file.
153 */
154static int current_syn_inc_tag = 0;
155static int running_syn_inc_tag = 0;
156
157/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000158 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
159 * This avoids adding a pointer to the hashtable item.
160 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
161 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
162 * HI2KE() converts a hashitem pointer to a var pointer.
163 */
164static keyentry_T dumkey;
165#define KE2HIKEY(kp) ((kp)->keyword)
166#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
167#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
168
169/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000170 * To reduce the time spent in keepend(), remember at which level in the state
171 * stack the first item with "keepend" is present. When "-1", there is no
172 * "keepend" on the stack.
173 */
174static int keepend_level = -1;
175
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200176static char msg_no_items[] = N_("No Syntax items defined for this buffer");
177
Bram Moolenaar071d4272004-06-13 20:20:40 +0000178/*
179 * For the current state we need to remember more than just the idx.
180 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
181 * (The end positions have the column number of the next char)
182 */
183typedef struct state_item
184{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100185 int si_idx; // index of syntax pattern or
186 // KEYWORD_IDX
187 int si_id; // highlight group ID for keywords
188 int si_trans_id; // idem, transparency removed
189 int si_m_lnum; // lnum of the match
190 int si_m_startcol; // starting column of the match
191 lpos_T si_m_endpos; // just after end posn of the match
192 lpos_T si_h_startpos; // start position of the highlighting
193 lpos_T si_h_endpos; // end position of the highlighting
194 lpos_T si_eoe_pos; // end position of end pattern
195 int si_end_idx; // group ID for end pattern or zero
196 int si_ends; // if match ends before si_m_endpos
197 int si_attr; // attributes in this state
198 long si_flags; // HL_HAS_EOL flag in this state, and
199 // HL_SKIP* for si_next_list
Bram Moolenaar860cae12010-06-05 23:22:07 +0200200#ifdef FEAT_CONCEAL
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100201 int si_seqnr; // sequence number
202 int si_cchar; // substitution character for conceal
Bram Moolenaar860cae12010-06-05 23:22:07 +0200203#endif
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100204 short *si_cont_list; // list of contained groups
205 short *si_next_list; // nextgroup IDs after this item ends
206 reg_extmatch_T *si_extmatch; // \z(...\) matches from start
207 // pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +0000208} stateitem_T;
209
kylo252ae6f1d82022-02-16 19:24:07 +0000210#define KEYWORD_IDX (-1) // value of si_idx for keywords
211#define ID_LIST_ALL ((short *)-1) // valid of si_cont_list for containing all
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100212 // but contained groups
Bram Moolenaar071d4272004-06-13 20:20:40 +0000213
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200214#ifdef FEAT_CONCEAL
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100215static int next_seqnr = 1; // value to use for si_seqnr
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200216#endif
217
Bram Moolenaar071d4272004-06-13 20:20:40 +0000218/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000219 * Struct to reduce the number of arguments to get_syn_options(), it's used
220 * very often.
221 */
222typedef struct
223{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100224 int flags; // flags for contained and transparent
225 int keyword; // TRUE for ":syn keyword"
226 int *sync_idx; // syntax item for "grouphere" argument, NULL
227 // if not allowed
228 char has_cont_list; // TRUE if "cont_list" can be used
229 short *cont_list; // group IDs for "contains" argument
230 short *cont_in_list; // group IDs for "containedin" argument
231 short *next_list; // group IDs for "nextgroup" argument
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000232} syn_opt_arg_T;
233
234/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000235 * The next possible match in the current line for any pattern is remembered,
236 * to avoid having to try for a match in each column.
237 * If next_match_idx == -1, not tried (in this line) yet.
238 * If next_match_col == MAXCOL, no match found in this line.
239 * (All end positions have the column of the char after the end)
240 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100241static int next_match_col; // column for start of next match
242static lpos_T next_match_m_endpos; // position for end of next match
243static lpos_T next_match_h_startpos; // pos. for highl. start of next match
244static lpos_T next_match_h_endpos; // pos. for highl. end of next match
245static int next_match_idx; // index of matched item
246static long next_match_flags; // flags for next match
247static lpos_T next_match_eos_pos; // end of start pattn (start region)
248static lpos_T next_match_eoe_pos; // pos. for end of end pattern
249static int next_match_end_idx; // ID of group for end pattn or zero
Bram Moolenaar071d4272004-06-13 20:20:40 +0000250static reg_extmatch_T *next_match_extmatch = NULL;
251
252/*
253 * A state stack is an array of integers or stateitem_T, stored in a
Bram Moolenaarf86db782018-10-25 13:31:37 +0200254 * garray_T. A state stack is invalid if its itemsize entry is zero.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000255 */
256#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
257#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
258
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200259#define FOR_ALL_SYNSTATES(sb, sst) \
260 for ((sst) = (sb)->b_sst_first; (sst) != NULL; (sst) = (sst)->sst_next)
261
Bram Moolenaar071d4272004-06-13 20:20:40 +0000262/*
263 * The current state (within the line) of the recognition engine.
264 * When current_state.ga_itemsize is 0 the current state is invalid.
265 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100266static win_T *syn_win; // current window for highlighting
267static buf_T *syn_buf; // current buffer for highlighting
268static synblock_T *syn_block; // current buffer for highlighting
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200269#ifdef FEAT_RELTIME
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100270static proftime_T *syn_tm; // timeout limit
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200271#endif
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100272static linenr_T current_lnum = 0; // lnum of current state
273static colnr_T current_col = 0; // column of current state
274static int current_state_stored = 0; // TRUE if stored current state
275 // after setting current_finished
276static int current_finished = 0; // current line has been finished
277static garray_T current_state // current stack of state_items
Bram Moolenaar071d4272004-06-13 20:20:40 +0000278 = {0, 0, 0, 0, NULL};
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100279static short *current_next_list = NULL; // when non-zero, nextgroup list
280static int current_next_flags = 0; // flags for current_next_list
281static int current_line_id = 0; // unique number for current line
Bram Moolenaar071d4272004-06-13 20:20:40 +0000282
283#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
284
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100285static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100286static int syn_match_linecont(linenr_T lnum);
287static void syn_start_line(void);
288static void syn_update_ends(int startofline);
289static void syn_stack_alloc(void);
290static int syn_stack_cleanup(void);
291static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
292static synstate_T *syn_stack_find_entry(linenr_T lnum);
293static synstate_T *store_current_state(void);
294static void load_current_state(synstate_T *from);
295static void invalidate_current_state(void);
296static int syn_stack_equal(synstate_T *sp);
297static void validate_current_state(void);
298static int syn_finish_line(int syncing);
299static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
300static int did_match_already(int idx, garray_T *gap);
301static stateitem_T *push_next_match(stateitem_T *cur_si);
302static void check_state_ends(void);
303static void update_si_attr(int idx);
304static void check_keepend(void);
305static void update_si_end(stateitem_T *sip, int startcol, int force);
306static short *copy_id_list(short *list);
307static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
308static int push_current_state(int idx);
309static void pop_current_state(void);
Bram Moolenaarf7512552013-06-06 14:55:19 +0200310#ifdef FEAT_PROFILE
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100311static void syn_clear_time(syn_time_T *tt);
312static void syntime_clear(void);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100313static void syntime_report(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200314static int syn_time_on = FALSE;
315# define IF_SYN_TIME(p) (p)
316#else
317# define IF_SYN_TIME(p) NULL
318typedef int syn_time_T;
319#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000320
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100321static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
322static 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 +0000323
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100324static void limit_pos(lpos_T *pos, lpos_T *limit);
325static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
326static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
327static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
328static char_u *syn_getcurline(void);
329static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
330static 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 +0100331static void syn_remove_pattern(synblock_T *block, int idx);
332static void syn_clear_pattern(synblock_T *block, int i);
333static void syn_clear_cluster(synblock_T *block, int i);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100334static void syn_clear_one(int id, int syncing);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100335static void syn_cmd_onoff(exarg_T *eap, char *name);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100336static void syn_lines_msg(void);
337static void syn_match_msg(void);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100338static void syn_list_one(int id, int syncing, int link_only);
339static void syn_list_cluster(int id);
340static void put_id_list(char_u *name, short *list, int attr);
341static void put_pattern(char *s, int c, synpat_T *spp, int attr);
342static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
343static void syn_clear_keyword(int id, hashtab_T *ht);
344static void clear_keywtab(hashtab_T *ht);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100345static int syn_scl_namen2id(char_u *linep, int len);
346static int syn_check_cluster(char_u *pp, int len);
347static int syn_add_cluster(char_u *name);
348static void init_syn_patterns(void);
349static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100350static int get_id_list(char_u **arg, int keylen, short **list, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100351static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000352
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200353#if defined(FEAT_RELTIME) || defined(PROTO)
354/*
355 * Set the timeout used for syntax highlighting.
356 * Use NULL to reset, no timeout.
357 */
358 void
359syn_set_timeout(proftime_T *tm)
360{
361 syn_tm = tm;
362}
363#endif
364
Bram Moolenaar071d4272004-06-13 20:20:40 +0000365/*
366 * Start the syntax recognition for a line. This function is normally called
367 * from the screen updating, once for each displayed line.
368 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
369 * it. Careful: curbuf and curwin are likely to point to another buffer and
370 * window.
371 */
372 void
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200373syntax_start(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000374{
375 synstate_T *p;
376 synstate_T *last_valid = NULL;
377 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000378 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000379 linenr_T parsed_lnum;
380 linenr_T first_stored;
381 int dist;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100382 static varnumber_T changedtick = 0; // remember the last change ID
Bram Moolenaar071d4272004-06-13 20:20:40 +0000383
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200384#ifdef FEAT_CONCEAL
385 current_sub_char = NUL;
386#endif
387
Bram Moolenaar071d4272004-06-13 20:20:40 +0000388 /*
389 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000390 * Also do this when a change was made, the current state may be invalid
391 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000392 */
Bram Moolenaarb681be12016-03-31 23:02:16 +0200393 if (syn_block != wp->w_s
394 || syn_buf != wp->w_buffer
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100395 || changedtick != CHANGEDTICK(syn_buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000396 {
397 invalidate_current_state();
398 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200399 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000400 }
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100401 changedtick = CHANGEDTICK(syn_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000402 syn_win = wp;
403
404 /*
405 * Allocate syntax stack when needed.
406 */
407 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200408 if (syn_block->b_sst_array == NULL)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100409 return; // out of memory
Bram Moolenaar860cae12010-06-05 23:22:07 +0200410 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000411
412 /*
413 * If the state of the end of the previous line is useful, store it.
414 */
415 if (VALID_STATE(&current_state)
416 && current_lnum < lnum
417 && current_lnum < syn_buf->b_ml.ml_line_count)
418 {
419 (void)syn_finish_line(FALSE);
420 if (!current_state_stored)
421 {
422 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000423 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000424 }
425
426 /*
427 * If the current_lnum is now the same as "lnum", keep the current
428 * state (this happens very often!). Otherwise invalidate
429 * current_state and figure it out below.
430 */
431 if (current_lnum != lnum)
432 invalidate_current_state();
433 }
434 else
435 invalidate_current_state();
436
437 /*
438 * Try to synchronize from a saved state in b_sst_array[].
439 * Only do this if lnum is not before and not to far beyond a saved state.
440 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200441 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000442 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100443 // Find last valid saved state before start_lnum.
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200444 FOR_ALL_SYNSTATES(syn_block, p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000445 {
446 if (p->sst_lnum > lnum)
447 break;
448 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
449 {
450 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200451 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000452 last_min_valid = p;
453 }
454 }
455 if (last_min_valid != NULL)
456 load_current_state(last_min_valid);
457 }
458
459 /*
460 * If "lnum" is before or far beyond a line with a saved state, need to
461 * re-synchronize.
462 */
463 if (INVALID_STATE(&current_state))
464 {
465 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200466 if (current_lnum == 1)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100467 // First line is always valid, no matter "minlines".
Bram Moolenaard6761c32011-06-19 04:54:21 +0200468 first_stored = 1;
469 else
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100470 // Need to parse "minlines" lines before state can be considered
471 // valid to store.
Bram Moolenaard6761c32011-06-19 04:54:21 +0200472 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000473 }
474 else
475 first_stored = current_lnum;
476
477 /*
478 * Advance from the sync point or saved state until the current line.
479 * Save some entries for syncing with later on.
480 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200481 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000482 dist = 999999;
483 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200484 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000485 while (current_lnum < lnum)
486 {
487 syn_start_line();
488 (void)syn_finish_line(FALSE);
489 ++current_lnum;
490
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100491 // If we parsed at least "minlines" lines or started at a valid
492 // state, the current state is considered valid.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000493 if (current_lnum >= first_stored)
494 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100495 // Check if the saved state entry is for the current line and is
496 // equal to the current state. If so, then validate all saved
497 // states that depended on a change before the parsed line.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000498 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000499 prev = syn_stack_find_entry(current_lnum - 1);
500 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200501 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000502 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000503 sp = prev;
504 while (sp != NULL && sp->sst_lnum < current_lnum)
505 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000506 if (sp != NULL
507 && sp->sst_lnum == current_lnum
508 && syn_stack_equal(sp))
509 {
510 parsed_lnum = current_lnum;
511 prev = sp;
512 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
513 {
514 if (sp->sst_lnum <= lnum)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100515 // valid state before desired line, use this one
Bram Moolenaar071d4272004-06-13 20:20:40 +0000516 prev = sp;
517 else if (sp->sst_change_lnum == 0)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100518 // past saved states depending on change, break here.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000519 break;
520 sp->sst_change_lnum = 0;
521 sp = sp->sst_next;
522 }
523 load_current_state(prev);
524 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100525 // Store the state at this line when it's the first one, the line
526 // where we start parsing, or some distance from the previously
527 // saved state. But only when parsed at least 'minlines'.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000528 else if (prev == NULL
529 || current_lnum == lnum
530 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000531 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000532 }
533
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100534 // This can take a long time: break when CTRL-C pressed. The current
535 // state will be wrong then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000536 line_breakcheck();
537 if (got_int)
538 {
539 current_lnum = lnum;
540 break;
541 }
542 }
543
544 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000545}
546
547/*
548 * We cannot simply discard growarrays full of state_items or buf_states; we
549 * have to manually release their extmatch pointers first.
550 */
551 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100552clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000553{
554 int i;
555 garray_T *gap;
556
557 if (p->sst_stacksize > SST_FIX_STATES)
558 {
559 gap = &(p->sst_union.sst_ga);
560 for (i = 0; i < gap->ga_len; i++)
561 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
562 ga_clear(gap);
563 }
564 else
565 {
566 for (i = 0; i < p->sst_stacksize; i++)
567 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
568 }
569}
570
571/*
572 * Cleanup the current_state stack.
573 */
574 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100575clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000576{
577 int i;
578 stateitem_T *sip;
579
580 sip = (stateitem_T *)(current_state.ga_data);
581 for (i = 0; i < current_state.ga_len; i++)
582 unref_extmatch(sip[i].si_extmatch);
583 ga_clear(&current_state);
584}
585
586/*
587 * Try to find a synchronisation point for line "lnum".
588 *
589 * This sets current_lnum and the current state. One of three methods is
590 * used:
591 * 1. Search backwards for the end of a C-comment.
592 * 2. Search backwards for given sync patterns.
593 * 3. Simply start on a given number of lines above "lnum".
594 */
595 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100596syn_sync(
597 win_T *wp,
598 linenr_T start_lnum,
599 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000600{
601 buf_T *curbuf_save;
602 win_T *curwin_save;
603 pos_T cursor_save;
604 int idx;
605 linenr_T lnum;
606 linenr_T end_lnum;
607 linenr_T break_lnum;
608 int had_sync_point;
609 stateitem_T *cur_si;
610 synpat_T *spp;
611 char_u *line;
612 int found_flags = 0;
613 int found_match_idx = 0;
614 linenr_T found_current_lnum = 0;
615 int found_current_col= 0;
616 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000617 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000618
619 /*
620 * Clear any current state that might be hanging around.
621 */
622 invalidate_current_state();
623
624 /*
625 * Start at least "minlines" back. Default starting point for parsing is
626 * there.
627 * Start further back, to avoid that scrolling backwards will result in
628 * resyncing for every line. Now it resyncs only one out of N lines,
629 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
630 * Watch out for overflow when minlines is MAXLNUM.
631 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200632 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000633 start_lnum = 1;
634 else
635 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200636 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000637 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200638 else if (syn_block->b_syn_sync_minlines < 10)
639 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000640 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200641 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
642 if (syn_block->b_syn_sync_maxlines != 0
643 && lnum > syn_block->b_syn_sync_maxlines)
644 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000645 if (lnum >= start_lnum)
646 start_lnum = 1;
647 else
648 start_lnum -= lnum;
649 }
650 current_lnum = start_lnum;
651
652 /*
653 * 1. Search backwards for the end of a C-style comment.
654 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200655 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000656 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100657 // Need to make syn_buf the current buffer for a moment, to be able to
658 // use find_start_comment().
Bram Moolenaar071d4272004-06-13 20:20:40 +0000659 curwin_save = curwin;
660 curwin = wp;
661 curbuf_save = curbuf;
662 curbuf = syn_buf;
663
664 /*
665 * Skip lines that end in a backslash.
666 */
667 for ( ; start_lnum > 1; --start_lnum)
668 {
669 line = ml_get(start_lnum - 1);
670 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
671 break;
672 }
673 current_lnum = start_lnum;
674
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100675 // set cursor to start of search
Bram Moolenaar071d4272004-06-13 20:20:40 +0000676 cursor_save = wp->w_cursor;
677 wp->w_cursor.lnum = start_lnum;
678 wp->w_cursor.col = 0;
679
680 /*
681 * If the line is inside a comment, need to find the syntax item that
682 * defines the comment.
683 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
684 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200685 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000686 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200687 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
688 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
689 == syn_block->b_syn_sync_id
690 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000691 {
692 validate_current_state();
693 if (push_current_state(idx) == OK)
694 update_si_attr(current_state.ga_len - 1);
695 break;
696 }
697 }
698
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100699 // restore cursor and buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +0000700 wp->w_cursor = cursor_save;
701 curwin = curwin_save;
702 curbuf = curbuf_save;
703 }
704
705 /*
706 * 2. Search backwards for given sync patterns.
707 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200708 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000709 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200710 if (syn_block->b_syn_sync_maxlines != 0
711 && start_lnum > syn_block->b_syn_sync_maxlines)
712 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000713 else
714 break_lnum = 0;
715
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000716 found_m_endpos.lnum = 0;
717 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000718 end_lnum = start_lnum;
719 lnum = start_lnum;
720 while (--lnum > break_lnum)
721 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100722 // This can take a long time: break when CTRL-C pressed.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000723 line_breakcheck();
724 if (got_int)
725 {
726 invalidate_current_state();
727 current_lnum = start_lnum;
728 break;
729 }
730
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100731 // Check if we have run into a valid saved state stack now.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000732 if (last_valid != NULL && lnum == last_valid->sst_lnum)
733 {
734 load_current_state(last_valid);
735 break;
736 }
737
738 /*
739 * Check if the previous line has the line-continuation pattern.
740 */
741 if (lnum > 1 && syn_match_linecont(lnum - 1))
742 continue;
743
744 /*
745 * Start with nothing on the state stack
746 */
747 validate_current_state();
748
749 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
750 {
751 syn_start_line();
752 for (;;)
753 {
754 had_sync_point = syn_finish_line(TRUE);
755 /*
756 * When a sync point has been found, remember where, and
757 * continue to look for another one, further on in the line.
758 */
759 if (had_sync_point && current_state.ga_len)
760 {
761 cur_si = &CUR_STATE(current_state.ga_len - 1);
762 if (cur_si->si_m_endpos.lnum > start_lnum)
763 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100764 // ignore match that goes to after where started
Bram Moolenaar071d4272004-06-13 20:20:40 +0000765 current_lnum = end_lnum;
766 break;
767 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000768 if (cur_si->si_idx < 0)
769 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100770 // Cannot happen?
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000771 found_flags = 0;
772 found_match_idx = KEYWORD_IDX;
773 }
774 else
775 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200776 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000777 found_flags = spp->sp_flags;
778 found_match_idx = spp->sp_sync_idx;
779 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000780 found_current_lnum = current_lnum;
781 found_current_col = current_col;
782 found_m_endpos = cur_si->si_m_endpos;
783 /*
784 * Continue after the match (be aware of a zero-length
785 * match).
786 */
787 if (found_m_endpos.lnum > current_lnum)
788 {
789 current_lnum = found_m_endpos.lnum;
790 current_col = found_m_endpos.col;
791 if (current_lnum >= end_lnum)
792 break;
793 }
794 else if (found_m_endpos.col > current_col)
795 current_col = found_m_endpos.col;
796 else
797 ++current_col;
798
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100799 // syn_current_attr() will have skipped the check for
800 // an item that ends here, need to do that now. Be
801 // careful not to go past the NUL.
Bram Moolenaar81366db2005-07-24 21:16:51 +0000802 prev_current_col = current_col;
803 if (syn_getcurline()[current_col] != NUL)
804 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000805 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000806 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000807 }
808 else
809 break;
810 }
811 }
812
813 /*
814 * If a sync point was encountered, break here.
815 */
816 if (found_flags)
817 {
818 /*
819 * Put the item that was specified by the sync point on the
820 * state stack. If there was no item specified, make the
821 * state stack empty.
822 */
823 clear_current_state();
824 if (found_match_idx >= 0
825 && push_current_state(found_match_idx) == OK)
826 update_si_attr(current_state.ga_len - 1);
827
828 /*
829 * When using "grouphere", continue from the sync point
830 * match, until the end of the line. Parsing starts at
831 * the next line.
832 * For "groupthere" the parsing starts at start_lnum.
833 */
834 if (found_flags & HL_SYNC_HERE)
835 {
836 if (current_state.ga_len)
837 {
838 cur_si = &CUR_STATE(current_state.ga_len - 1);
839 cur_si->si_h_startpos.lnum = found_current_lnum;
840 cur_si->si_h_startpos.col = found_current_col;
841 update_si_end(cur_si, (int)current_col, TRUE);
842 check_keepend();
843 }
844 current_col = found_m_endpos.col;
845 current_lnum = found_m_endpos.lnum;
846 (void)syn_finish_line(FALSE);
847 ++current_lnum;
848 }
849 else
850 current_lnum = start_lnum;
851
852 break;
853 }
854
855 end_lnum = lnum;
856 invalidate_current_state();
857 }
858
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100859 // Ran into start of the file or exceeded maximum number of lines
Bram Moolenaar071d4272004-06-13 20:20:40 +0000860 if (lnum <= break_lnum)
861 {
862 invalidate_current_state();
863 current_lnum = break_lnum + 1;
864 }
865 }
866
867 validate_current_state();
868}
869
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100870 static void
871save_chartab(char_u *chartab)
872{
873 if (syn_block->b_syn_isk != empty_option)
874 {
875 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
876 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
877 (size_t)32);
878 }
879}
880
881 static void
882restore_chartab(char_u *chartab)
883{
884 if (syn_win->w_s->b_syn_isk != empty_option)
885 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
886}
887
Bram Moolenaar071d4272004-06-13 20:20:40 +0000888/*
889 * Return TRUE if the line-continuation pattern matches in line "lnum".
890 */
891 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100892syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000893{
894 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100895 int r;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100896 char_u buf_chartab[32]; // chartab array for syn iskyeyword
Bram Moolenaar071d4272004-06-13 20:20:40 +0000897
Bram Moolenaar860cae12010-06-05 23:22:07 +0200898 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000899 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100900 // use syntax iskeyword option
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100901 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200902 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
903 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100904 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200905 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100906 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100907 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100908 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000909 }
910 return FALSE;
911}
912
913/*
914 * Prepare the current state for the start of a line.
915 */
916 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100917syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000918{
919 current_finished = FALSE;
920 current_col = 0;
921
922 /*
923 * Need to update the end of a start/skip/end that continues from the
924 * previous line and regions that have "keepend".
925 */
926 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +0200927 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000928 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +0200929 check_state_ends();
930 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000931
932 next_match_idx = -1;
933 ++current_line_id;
Bram Moolenaarea20de82017-06-24 22:52:24 +0200934#ifdef FEAT_CONCEAL
Bram Moolenaarcc0750d2017-06-24 22:29:24 +0200935 next_seqnr = 1;
Bram Moolenaarea20de82017-06-24 22:52:24 +0200936#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000937}
938
939/*
940 * Check for items in the stack that need their end updated.
941 * When "startofline" is TRUE the last item is always updated.
942 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
943 */
944 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100945syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000946{
947 stateitem_T *cur_si;
948 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000949 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000950
951 if (startofline)
952 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100953 // Check for a match carried over from a previous line with a
954 // contained region. The match ends as soon as the region ends.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000955 for (i = 0; i < current_state.ga_len; ++i)
956 {
957 cur_si = &CUR_STATE(i);
958 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +0200959 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +0000960 == SPTYPE_MATCH
961 && cur_si->si_m_endpos.lnum < current_lnum)
962 {
963 cur_si->si_flags |= HL_MATCHCONT;
964 cur_si->si_m_endpos.lnum = 0;
965 cur_si->si_m_endpos.col = 0;
966 cur_si->si_h_endpos = cur_si->si_m_endpos;
967 cur_si->si_ends = TRUE;
968 }
969 }
970 }
971
972 /*
973 * Need to update the end of a start/skip/end that continues from the
974 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000975 * influence contained items. If we've just removed "extend"
976 * (startofline == 0) then we should update ends of normal regions
977 * contained inside "keepend" because "extend" could have extended
978 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000979 * Then check for items ending in column 0.
980 */
981 i = current_state.ga_len - 1;
982 if (keepend_level >= 0)
983 for ( ; i > keepend_level; --i)
984 if (CUR_STATE(i).si_flags & HL_EXTEND)
985 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000986
987 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000988 for ( ; i < current_state.ga_len; ++i)
989 {
990 cur_si = &CUR_STATE(i);
991 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000992 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000993 || (i == current_state.ga_len - 1 && startofline))
994 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100995 cur_si->si_h_startpos.col = 0; // start highl. in col 0
Bram Moolenaar071d4272004-06-13 20:20:40 +0000996 cur_si->si_h_startpos.lnum = current_lnum;
997
998 if (!(cur_si->si_flags & HL_MATCHCONT))
999 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001000
1001 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1002 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001003 }
1004 }
1005 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001006}
1007
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001008/////////////////////////////////////////
1009// Handling of the state stack cache.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001010
1011/*
1012 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1013 *
1014 * To speed up syntax highlighting, the state stack for the start of some
1015 * lines is cached. These entries can be used to start parsing at that point.
1016 *
1017 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1018 * valid entries. b_sst_first points to the first one, then follow sst_next.
1019 * The entries are sorted on line number. The first entry is often for line 2
1020 * (line 1 always starts with an empty stack).
1021 * There is also a list for free entries. This construction is used to avoid
1022 * having to allocate and free memory blocks too often.
1023 *
1024 * When making changes to the buffer, this is logged in b_mod_*. When calling
1025 * update_screen() to update the display, it will call
1026 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1027 * entries. The entries which are inside the changed area are removed,
1028 * because they must be recomputed. Entries below the changed have their line
1029 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1030 * set to indicate that a check must be made if the changed lines would change
1031 * the cached entry.
1032 *
1033 * When later displaying lines, an entry is stored for each line. Displayed
1034 * lines are likely to be displayed again, in which case the state at the
1035 * start of the line is needed.
1036 * For not displayed lines, an entry is stored for every so many lines. These
1037 * entries will be used e.g., when scrolling backwards. The distance between
1038 * entries depends on the number of lines in the buffer. For small buffers
1039 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1040 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1041 */
1042
Bram Moolenaar860cae12010-06-05 23:22:07 +02001043 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001044syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001045{
1046 synstate_T *p;
1047
1048 if (block->b_sst_array != NULL)
1049 {
Bram Moolenaar00d253e2020-04-06 22:13:01 +02001050 FOR_ALL_SYNSTATES(block, p)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001051 clear_syn_state(p);
Bram Moolenaard23a8232018-02-10 18:45:26 +01001052 VIM_CLEAR(block->b_sst_array);
Bram Moolenaar95892c22018-09-28 22:26:54 +02001053 block->b_sst_first = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001054 block->b_sst_len = 0;
1055 }
1056}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001057/*
1058 * Free b_sst_array[] for buffer "buf".
1059 * Used when syntax items changed to force resyncing everywhere.
1060 */
1061 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001062syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001063{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001064#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001065 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001066#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001067
Bram Moolenaar860cae12010-06-05 23:22:07 +02001068 syn_stack_free_block(block);
1069
Bram Moolenaar071d4272004-06-13 20:20:40 +00001070#ifdef FEAT_FOLDING
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001071 // When using "syntax" fold method, must update all folds.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001072 FOR_ALL_WINDOWS(wp)
1073 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001074 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001075 foldUpdateAll(wp);
1076 }
1077#endif
1078}
1079
1080/*
1081 * Allocate the syntax state stack for syn_buf when needed.
1082 * If the number of entries in b_sst_array[] is much too big or a bit too
1083 * small, reallocate it.
1084 * Also used to allocate b_sst_array[] for the first time.
1085 */
1086 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001087syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001088{
1089 long len;
1090 synstate_T *to, *from;
1091 synstate_T *sstp;
1092
1093 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1094 if (len < SST_MIN_ENTRIES)
1095 len = SST_MIN_ENTRIES;
1096 else if (len > SST_MAX_ENTRIES)
1097 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001098 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001099 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001100 // Allocate 50% too much, to avoid reallocating too often.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001101 len = syn_buf->b_ml.ml_line_count;
1102 len = (len + len / 2) / SST_DIST + Rows * 2;
1103 if (len < SST_MIN_ENTRIES)
1104 len = SST_MIN_ENTRIES;
1105 else if (len > SST_MAX_ENTRIES)
1106 len = SST_MAX_ENTRIES;
1107
Bram Moolenaar860cae12010-06-05 23:22:07 +02001108 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001109 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001110 // When shrinking the array, cleanup the existing stack.
1111 // Make sure that all valid entries fit in the new array.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001112 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001113 && syn_stack_cleanup())
1114 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001115 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1116 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001117 }
1118
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001119 sstp = ALLOC_CLEAR_MULT(synstate_T, len);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001120 if (sstp == NULL) // out of memory!
Bram Moolenaar071d4272004-06-13 20:20:40 +00001121 return;
1122
1123 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001124 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001125 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001126 // Move the states from the old array to the new one.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001127 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001128 from = from->sst_next)
1129 {
1130 ++to;
1131 *to = *from;
1132 to->sst_next = to + 1;
1133 }
1134 }
1135 if (to != sstp - 1)
1136 {
1137 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001138 syn_block->b_sst_first = sstp;
1139 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001140 }
1141 else
1142 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001143 syn_block->b_sst_first = NULL;
1144 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001145 }
1146
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001147 // Create the list of free entries.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001148 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001149 while (++to < sstp + len)
1150 to->sst_next = to + 1;
1151 (sstp + len - 1)->sst_next = NULL;
1152
Bram Moolenaar860cae12010-06-05 23:22:07 +02001153 vim_free(syn_block->b_sst_array);
1154 syn_block->b_sst_array = sstp;
1155 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001156 }
1157}
1158
1159/*
1160 * Check for changes in a buffer to affect stored syntax states. Uses the
1161 * b_mod_* fields.
1162 * Called from update_screen(), before screen is being updated, once for each
1163 * displayed buffer.
1164 */
1165 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001166syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001167{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001168 win_T *wp;
1169
1170 syn_stack_apply_changes_block(&buf->b_s, buf);
1171
1172 FOR_ALL_WINDOWS(wp)
1173 {
1174 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1175 syn_stack_apply_changes_block(wp->w_s, buf);
1176 }
1177}
1178
1179 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001180syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001181{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001182 synstate_T *p, *prev, *np;
1183 linenr_T n;
1184
Bram Moolenaar071d4272004-06-13 20:20:40 +00001185 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001186 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001187 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001188 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001189 {
1190 n = p->sst_lnum + buf->b_mod_xlines;
1191 if (n <= buf->b_mod_bot)
1192 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001193 // this state is inside the changed area, remove it
Bram Moolenaar071d4272004-06-13 20:20:40 +00001194 np = p->sst_next;
1195 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001196 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001197 else
1198 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001199 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001200 p = np;
1201 continue;
1202 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001203 // This state is below the changed area. Remember the line
1204 // that needs to be parsed before this entry can be made valid
1205 // again.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001206 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1207 {
1208 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1209 p->sst_change_lnum += buf->b_mod_xlines;
1210 else
1211 p->sst_change_lnum = buf->b_mod_top;
1212 }
1213 if (p->sst_change_lnum == 0
1214 || p->sst_change_lnum < buf->b_mod_bot)
1215 p->sst_change_lnum = buf->b_mod_bot;
1216
1217 p->sst_lnum = n;
1218 }
1219 prev = p;
1220 p = p->sst_next;
1221 }
1222}
1223
1224/*
1225 * Reduce the number of entries in the state stack for syn_buf.
1226 * Returns TRUE if at least one entry was freed.
1227 */
1228 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001229syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001230{
1231 synstate_T *p, *prev;
1232 disptick_T tick;
1233 int above;
1234 int dist;
1235 int retval = FALSE;
1236
Bram Moolenaar95892c22018-09-28 22:26:54 +02001237 if (syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001238 return retval;
1239
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001240 // Compute normal distance between non-displayed entries.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001241 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001242 dist = 999999;
1243 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001244 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001245
1246 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001247 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001248 * be removed. Set "above" when the "tick" for the oldest entry is above
1249 * "b_sst_lasttick" (the display tick wraps around).
1250 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001251 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001252 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001253 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1255 {
1256 if (prev->sst_lnum + dist > p->sst_lnum)
1257 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001258 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001259 {
1260 if (!above || p->sst_tick < tick)
1261 tick = p->sst_tick;
1262 above = TRUE;
1263 }
1264 else if (!above && p->sst_tick < tick)
1265 tick = p->sst_tick;
1266 }
1267 }
1268
1269 /*
1270 * Go through the list to make the entries for the oldest tick at an
1271 * interval of several lines.
1272 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001273 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001274 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1275 {
1276 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1277 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001278 // Move this entry from used list to free list
Bram Moolenaar071d4272004-06-13 20:20:40 +00001279 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001280 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001281 p = prev;
1282 retval = TRUE;
1283 }
1284 }
1285 return retval;
1286}
1287
1288/*
1289 * Free the allocated memory for a syn_state item.
1290 * Move the entry into the free list.
1291 */
1292 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001293syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001294{
1295 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001296 p->sst_next = block->b_sst_firstfree;
1297 block->b_sst_firstfree = p;
1298 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001299}
1300
1301/*
1302 * Find an entry in the list of state stacks at or before "lnum".
1303 * Returns NULL when there is no entry or the first entry is after "lnum".
1304 */
1305 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001306syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001307{
1308 synstate_T *p, *prev;
1309
1310 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001311 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001312 {
1313 if (p->sst_lnum == lnum)
1314 return p;
1315 if (p->sst_lnum > lnum)
1316 break;
1317 }
1318 return prev;
1319}
1320
1321/*
1322 * Try saving the current state in b_sst_array[].
1323 * The current state must be valid for the start of the current_lnum line!
1324 */
1325 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001326store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001327{
1328 int i;
1329 synstate_T *p;
1330 bufstate_T *bp;
1331 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001332 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001333
1334 /*
1335 * If the current state contains a start or end pattern that continues
1336 * from the previous line, we can't use it. Don't store it then.
1337 */
1338 for (i = current_state.ga_len - 1; i >= 0; --i)
1339 {
1340 cur_si = &CUR_STATE(i);
1341 if (cur_si->si_h_startpos.lnum >= current_lnum
1342 || cur_si->si_m_endpos.lnum >= current_lnum
1343 || cur_si->si_h_endpos.lnum >= current_lnum
1344 || (cur_si->si_end_idx
1345 && cur_si->si_eoe_pos.lnum >= current_lnum))
1346 break;
1347 }
1348 if (i >= 0)
1349 {
1350 if (sp != NULL)
1351 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001352 // find "sp" in the list and remove it
Bram Moolenaar860cae12010-06-05 23:22:07 +02001353 if (syn_block->b_sst_first == sp)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001354 // it's the first entry
Bram Moolenaar860cae12010-06-05 23:22:07 +02001355 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001356 else
1357 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001358 // find the entry just before this one to adjust sst_next
Bram Moolenaar00d253e2020-04-06 22:13:01 +02001359 FOR_ALL_SYNSTATES(syn_block, p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001360 if (p->sst_next == sp)
1361 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001362 if (p != NULL) // just in case
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001363 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001364 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001365 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001366 sp = NULL;
1367 }
1368 }
1369 else if (sp == NULL || sp->sst_lnum != current_lnum)
1370 {
1371 /*
1372 * Add a new entry
1373 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001374 // If no free items, cleanup the array first.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001375 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001376 {
1377 (void)syn_stack_cleanup();
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001378 // "sp" may have been moved to the freelist now
Bram Moolenaar071d4272004-06-13 20:20:40 +00001379 sp = syn_stack_find_entry(current_lnum);
1380 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001381 // Still no free items? Must be a strange problem...
Bram Moolenaar860cae12010-06-05 23:22:07 +02001382 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001383 sp = NULL;
1384 else
1385 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001386 // Take the first item from the free list and put it in the used
1387 // list, after *sp
Bram Moolenaar860cae12010-06-05 23:22:07 +02001388 p = syn_block->b_sst_firstfree;
1389 syn_block->b_sst_firstfree = p->sst_next;
1390 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001391 if (sp == NULL)
1392 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001393 // Insert in front of the list
Bram Moolenaar860cae12010-06-05 23:22:07 +02001394 p->sst_next = syn_block->b_sst_first;
1395 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001396 }
1397 else
1398 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001399 // insert in list after *sp
Bram Moolenaar071d4272004-06-13 20:20:40 +00001400 p->sst_next = sp->sst_next;
1401 sp->sst_next = p;
1402 }
1403 sp = p;
1404 sp->sst_stacksize = 0;
1405 sp->sst_lnum = current_lnum;
1406 }
1407 }
1408 if (sp != NULL)
1409 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001410 // When overwriting an existing state stack, clear it first
Bram Moolenaar071d4272004-06-13 20:20:40 +00001411 clear_syn_state(sp);
1412 sp->sst_stacksize = current_state.ga_len;
1413 if (current_state.ga_len > SST_FIX_STATES)
1414 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001415 // Need to clear it, might be something remaining from when the
1416 // length was less than SST_FIX_STATES.
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001417 ga_init2(&sp->sst_union.sst_ga, sizeof(bufstate_T), 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001418 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1419 sp->sst_stacksize = 0;
1420 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001421 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001422 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1423 }
1424 else
1425 bp = sp->sst_union.sst_stack;
1426 for (i = 0; i < sp->sst_stacksize; ++i)
1427 {
1428 bp[i].bs_idx = CUR_STATE(i).si_idx;
1429 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001430#ifdef FEAT_CONCEAL
1431 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1432 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1433#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001434 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1435 }
1436 sp->sst_next_flags = current_next_flags;
1437 sp->sst_next_list = current_next_list;
1438 sp->sst_tick = display_tick;
1439 sp->sst_change_lnum = 0;
1440 }
1441 current_state_stored = TRUE;
1442 return sp;
1443}
1444
1445/*
1446 * Copy a state stack from "from" in b_sst_array[] to current_state;
1447 */
1448 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001449load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001450{
1451 int i;
1452 bufstate_T *bp;
1453
1454 clear_current_state();
1455 validate_current_state();
1456 keepend_level = -1;
1457 if (from->sst_stacksize
1458 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1459 {
1460 if (from->sst_stacksize > SST_FIX_STATES)
1461 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1462 else
1463 bp = from->sst_union.sst_stack;
1464 for (i = 0; i < from->sst_stacksize; ++i)
1465 {
1466 CUR_STATE(i).si_idx = bp[i].bs_idx;
1467 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001468#ifdef FEAT_CONCEAL
1469 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1470 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1471#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001472 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1473 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1474 keepend_level = i;
1475 CUR_STATE(i).si_ends = FALSE;
1476 CUR_STATE(i).si_m_lnum = 0;
1477 if (CUR_STATE(i).si_idx >= 0)
1478 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001479 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001480 else
1481 CUR_STATE(i).si_next_list = NULL;
1482 update_si_attr(i);
1483 }
1484 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001485 }
1486 current_next_list = from->sst_next_list;
1487 current_next_flags = from->sst_next_flags;
1488 current_lnum = from->sst_lnum;
1489}
1490
1491/*
1492 * Compare saved state stack "*sp" with the current state.
1493 * Return TRUE when they are equal.
1494 */
1495 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001496syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001497{
1498 int i, j;
1499 bufstate_T *bp;
1500 reg_extmatch_T *six, *bsx;
1501
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001502 // First a quick check if the stacks have the same size end nextlist.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001503 if (sp->sst_stacksize == current_state.ga_len
1504 && sp->sst_next_list == current_next_list)
1505 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001506 // Need to compare all states on both stacks.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001507 if (sp->sst_stacksize > SST_FIX_STATES)
1508 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1509 else
1510 bp = sp->sst_union.sst_stack;
1511
1512 for (i = current_state.ga_len; --i >= 0; )
1513 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001514 // If the item has another index the state is different.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001515 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1516 break;
1517 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1518 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001519 // When the extmatch pointers are different, the strings in
1520 // them can still be the same. Check if the extmatch
1521 // references are equal.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001522 bsx = bp[i].bs_extmatch;
1523 six = CUR_STATE(i).si_extmatch;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001524 // If one of the extmatch pointers is NULL the states are
1525 // different.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001526 if (bsx == NULL || six == NULL)
1527 break;
1528 for (j = 0; j < NSUBEXP; ++j)
1529 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001530 // Check each referenced match string. They must all be
1531 // equal.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001532 if (bsx->matches[j] != six->matches[j])
1533 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001534 // If the pointer is different it can still be the
1535 // same text. Compare the strings, ignore case when
1536 // the start item has the sp_ic flag set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001537 if (bsx->matches[j] == NULL
1538 || six->matches[j] == NULL)
1539 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001540 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001541 ? MB_STRICMP(bsx->matches[j],
1542 six->matches[j]) != 0
1543 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1544 break;
1545 }
1546 }
1547 if (j != NSUBEXP)
1548 break;
1549 }
1550 }
1551 if (i < 0)
1552 return TRUE;
1553 }
1554 return FALSE;
1555}
1556
1557/*
1558 * We stop parsing syntax above line "lnum". If the stored state at or below
1559 * this line depended on a change before it, it now depends on the line below
1560 * the last parsed line.
1561 * The window looks like this:
1562 * line which changed
1563 * displayed line
1564 * displayed line
1565 * lnum -> line below window
1566 */
1567 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001568syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001569{
1570 synstate_T *sp;
1571
1572 sp = syn_stack_find_entry(lnum);
1573 if (sp != NULL && sp->sst_lnum < lnum)
1574 sp = sp->sst_next;
1575
1576 if (sp != NULL && sp->sst_change_lnum != 0)
1577 sp->sst_change_lnum = lnum;
1578}
1579
1580/*
1581 * End of handling of the state stack.
1582 ****************************************/
1583
1584 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001585invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001586{
1587 clear_current_state();
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001588 current_state.ga_itemsize = 0; // mark current_state invalid
Bram Moolenaar071d4272004-06-13 20:20:40 +00001589 current_next_list = NULL;
1590 keepend_level = -1;
1591}
1592
1593 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001594validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001595{
1596 current_state.ga_itemsize = sizeof(stateitem_T);
1597 current_state.ga_growsize = 3;
1598}
1599
1600/*
1601 * Return TRUE if the syntax at start of lnum changed since last time.
1602 * This will only be called just after get_syntax_attr() for the previous
1603 * line, to check if the next line needs to be redrawn too.
1604 */
1605 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001606syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001607{
1608 int retval = TRUE;
1609 synstate_T *sp;
1610
Bram Moolenaar071d4272004-06-13 20:20:40 +00001611 /*
1612 * Check the state stack when:
1613 * - lnum is just below the previously syntaxed line.
1614 * - lnum is not before the lines with saved states.
1615 * - lnum is not past the lines with saved states.
1616 * - lnum is at or before the last changed line.
1617 */
1618 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1619 {
1620 sp = syn_stack_find_entry(lnum);
1621 if (sp != NULL && sp->sst_lnum == lnum)
1622 {
1623 /*
1624 * finish the previous line (needed when not all of the line was
1625 * drawn)
1626 */
1627 (void)syn_finish_line(FALSE);
1628
1629 /*
1630 * Compare the current state with the previously saved state of
1631 * the line.
1632 */
1633 if (syn_stack_equal(sp))
1634 retval = FALSE;
1635
1636 /*
1637 * Store the current state in b_sst_array[] for later use.
1638 */
1639 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001640 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001641 }
1642 }
1643
Bram Moolenaar071d4272004-06-13 20:20:40 +00001644 return retval;
1645}
1646
1647/*
1648 * Finish the current line.
1649 * This doesn't return any attributes, it only gets the state at the end of
1650 * the line. It can start anywhere in the line, as long as the current state
1651 * is valid.
1652 */
1653 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001654syn_finish_line(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001655 int syncing) // called for syncing
Bram Moolenaar071d4272004-06-13 20:20:40 +00001656{
1657 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001658 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001659
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001660 while (!current_finished)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001661 {
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001662 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1663 /*
1664 * When syncing, and found some item, need to check the item.
1665 */
1666 if (syncing && current_state.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001667 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001668 /*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001669 * Check for match with sync item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001670 */
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001671 cur_si = &CUR_STATE(current_state.ga_len - 1);
1672 if (cur_si->si_idx >= 0
1673 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1674 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1675 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001676
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001677 // syn_current_attr() will have skipped the check for an item
1678 // that ends here, need to do that now. Be careful not to go
1679 // past the NUL.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001680 prev_current_col = current_col;
1681 if (syn_getcurline()[current_col] != NUL)
1682 ++current_col;
1683 check_state_ends();
1684 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001685 }
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001686 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001687 }
1688 return FALSE;
1689}
1690
1691/*
1692 * Return highlight attributes for next character.
1693 * Must first call syntax_start() once for the line.
1694 * "col" is normally 0 for the first use in a line, and increments by one each
1695 * time. It's allowed to skip characters and to stop before the end of the
1696 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001697 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1698 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001699 */
1700 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001701get_syntax_attr(
1702 colnr_T col,
1703 int *can_spell,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001704 int keep_state) // keep state of char at "col"
Bram Moolenaar071d4272004-06-13 20:20:40 +00001705{
1706 int attr = 0;
1707
Bram Moolenaar349955a2007-08-14 21:07:36 +00001708 if (can_spell != NULL)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001709 // Default: Only do spelling when there is no @Spell cluster or when
1710 // ":syn spell toplevel" was used.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001711 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1712 ? (syn_block->b_spell_cluster_id == 0)
1713 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001714
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001715 // check for out of memory situation
Bram Moolenaar860cae12010-06-05 23:22:07 +02001716 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001717 return 0;
1718
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001719 // After 'synmaxcol' the attribute is always zero.
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001720 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001721 {
1722 clear_current_state();
1723#ifdef FEAT_EVAL
1724 current_id = 0;
1725 current_trans_id = 0;
1726#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001727#ifdef FEAT_CONCEAL
1728 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001729 current_seqnr = 0;
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001730#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001731 return 0;
1732 }
1733
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001734 // Make sure current_state is valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00001735 if (INVALID_STATE(&current_state))
1736 validate_current_state();
1737
1738 /*
1739 * Skip from the current column to "col", get the attributes for "col".
1740 */
1741 while (current_col <= col)
1742 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001743 attr = syn_current_attr(FALSE, TRUE, can_spell,
1744 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001745 ++current_col;
1746 }
1747
Bram Moolenaar071d4272004-06-13 20:20:40 +00001748 return attr;
1749}
1750
1751/*
1752 * Get syntax attributes for current_lnum, current_col.
1753 */
1754 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001755syn_current_attr(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001756 int syncing, // When 1: called for syncing
1757 int displaying, // result will be displayed
1758 int *can_spell, // return: do spell checking
1759 int keep_state) // keep syntax stack afterwards
Bram Moolenaar071d4272004-06-13 20:20:40 +00001760{
1761 int syn_id;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001762 lpos_T endpos; // was: char_u *endp;
1763 lpos_T hl_startpos; // was: int hl_startcol;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001764 lpos_T hl_endpos;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001765 lpos_T eos_pos; // end-of-start match (start region)
1766 lpos_T eoe_pos; // end-of-end pattern
1767 int end_idx; // group ID for end pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00001768 int idx;
1769 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001770 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001771 int startcol;
1772 int endcol;
1773 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001774 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001775 short *next_list;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001776 int found_match; // found usable match
1777 static int try_next_column = FALSE; // must try in next col
Bram Moolenaar071d4272004-06-13 20:20:40 +00001778 int do_keywords;
1779 regmmatch_T regmatch;
1780 lpos_T pos;
1781 int lc_col;
1782 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001783 char_u buf_chartab[32]; // chartab array for syn iskyeyword
1784 char_u *line; // current line. NOTE: becomes invalid after
1785 // looking for a pattern match!
Bram Moolenaar071d4272004-06-13 20:20:40 +00001786
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001787 // variables for zero-width matches that have a "nextgroup" argument
Bram Moolenaar071d4272004-06-13 20:20:40 +00001788 int keep_next_list;
1789 int zero_width_next_list = FALSE;
1790 garray_T zero_width_next_ga;
1791
1792 /*
1793 * No character, no attributes! Past end of line?
1794 * Do try matching with an empty line (could be the start of a region).
1795 */
1796 line = syn_getcurline();
1797 if (line[current_col] == NUL && current_col != 0)
1798 {
1799 /*
1800 * If we found a match after the last column, use it.
1801 */
1802 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1803 && next_match_col != MAXCOL)
1804 (void)push_next_match(NULL);
1805
1806 current_finished = TRUE;
1807 current_state_stored = FALSE;
1808 return 0;
1809 }
1810
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001811 // if the current or next character is NUL, we will finish the line now
Bram Moolenaar071d4272004-06-13 20:20:40 +00001812 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1813 {
1814 current_finished = TRUE;
1815 current_state_stored = FALSE;
1816 }
1817
1818 /*
1819 * When in the previous column there was a match but it could not be used
1820 * (empty match or already matched in this column) need to try again in
1821 * the next column.
1822 */
1823 if (try_next_column)
1824 {
1825 next_match_idx = -1;
1826 try_next_column = FALSE;
1827 }
1828
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001829 // Only check for keywords when not syncing and there are some.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001830 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001831 && (syn_block->b_keywtab.ht_used > 0
1832 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001833
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001834 // Init the list of zero-width matches with a nextlist. This is used to
1835 // avoid matching the same item in the same position twice.
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001836 ga_init2(&zero_width_next_ga, sizeof(int), 10);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001837
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001838 // use syntax iskeyword option
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001839 save_chartab(buf_chartab);
1840
Bram Moolenaar071d4272004-06-13 20:20:40 +00001841 /*
1842 * Repeat matching keywords and patterns, to find contained items at the
1843 * same column. This stops when there are no extra matches at the current
1844 * column.
1845 */
1846 do
1847 {
1848 found_match = FALSE;
1849 keep_next_list = FALSE;
1850 syn_id = 0;
1851
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001852
Bram Moolenaar071d4272004-06-13 20:20:40 +00001853 /*
1854 * 1. Check for a current state.
1855 * Only when there is no current state, or if the current state may
1856 * contain other things, we need to check for keywords and patterns.
1857 * Always need to check for contained items if some item has the
1858 * "containedin" argument (takes extra time!).
1859 */
1860 if (current_state.ga_len)
1861 cur_si = &CUR_STATE(current_state.ga_len - 1);
1862 else
1863 cur_si = NULL;
1864
Bram Moolenaar860cae12010-06-05 23:22:07 +02001865 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001866 || cur_si->si_cont_list != NULL)
1867 {
1868 /*
1869 * 2. Check for keywords, if on a keyword char after a non-keyword
1870 * char. Don't do this when syncing.
1871 */
1872 if (do_keywords)
1873 {
1874 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001875 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001876 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001877 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00001878 - (has_mbyte
1879 ? (*mb_head_off)(line, line + current_col - 1)
Bram Moolenaar264b74f2019-01-24 17:18:42 +01001880 : 0) , syn_buf)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001881 {
1882 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001883 &endcol, &flags, &next_list, cur_si,
1884 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001885 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001886 {
1887 if (push_current_state(KEYWORD_IDX) == OK)
1888 {
1889 cur_si = &CUR_STATE(current_state.ga_len - 1);
1890 cur_si->si_m_startcol = current_col;
1891 cur_si->si_h_startpos.lnum = current_lnum;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001892 cur_si->si_h_startpos.col = 0; // starts right away
Bram Moolenaar071d4272004-06-13 20:20:40 +00001893 cur_si->si_m_endpos.lnum = current_lnum;
1894 cur_si->si_m_endpos.col = endcol;
1895 cur_si->si_h_endpos.lnum = current_lnum;
1896 cur_si->si_h_endpos.col = endcol;
1897 cur_si->si_ends = TRUE;
1898 cur_si->si_end_idx = 0;
1899 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001900#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02001901 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001902 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001903 if (current_state.ga_len > 1)
1904 cur_si->si_flags |=
1905 CUR_STATE(current_state.ga_len - 2).si_flags
1906 & HL_CONCEAL;
1907#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001908 cur_si->si_id = syn_id;
1909 cur_si->si_trans_id = syn_id;
1910 if (flags & HL_TRANSP)
1911 {
1912 if (current_state.ga_len < 2)
1913 {
1914 cur_si->si_attr = 0;
1915 cur_si->si_trans_id = 0;
1916 }
1917 else
1918 {
1919 cur_si->si_attr = CUR_STATE(
1920 current_state.ga_len - 2).si_attr;
1921 cur_si->si_trans_id = CUR_STATE(
1922 current_state.ga_len - 2).si_trans_id;
1923 }
1924 }
1925 else
1926 cur_si->si_attr = syn_id2attr(syn_id);
1927 cur_si->si_cont_list = NULL;
1928 cur_si->si_next_list = next_list;
1929 check_keepend();
1930 }
1931 else
1932 vim_free(next_list);
1933 }
1934 }
1935 }
1936
1937 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001938 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001939 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001940 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001941 {
1942 /*
1943 * If we didn't check for a match yet, or we are past it, check
1944 * for any match with a pattern.
1945 */
1946 if (next_match_idx < 0 || next_match_col < (int)current_col)
1947 {
1948 /*
1949 * Check all relevant patterns for a match at this
1950 * position. This is complicated, because matching with a
1951 * pattern takes quite a bit of time, thus we want to
1952 * avoid doing it when it's not needed.
1953 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001954 next_match_idx = 0; // no match in this line yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00001955 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001956 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001957 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001958 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001959 if ( spp->sp_syncing == syncing
1960 && (displaying || !(spp->sp_flags & HL_DISPLAY))
1961 && (spp->sp_type == SPTYPE_MATCH
1962 || spp->sp_type == SPTYPE_START)
1963 && (current_next_list != NULL
1964 ? in_id_list(NULL, current_next_list,
1965 &spp->sp_syn, 0)
1966 : (cur_si == NULL
1967 ? !(spp->sp_flags & HL_CONTAINED)
1968 : in_id_list(cur_si,
1969 cur_si->si_cont_list, &spp->sp_syn,
1970 spp->sp_flags & HL_CONTAINED))))
1971 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001972 int r;
1973
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001974 // If we already tried matching in this line, and
1975 // there isn't a match before next_match_col, skip
1976 // this item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001977 if (spp->sp_line_id == current_line_id
1978 && spp->sp_startcol >= next_match_col)
1979 continue;
1980 spp->sp_line_id = current_line_id;
1981
1982 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
1983 if (lc_col < 0)
1984 lc_col = 0;
1985
1986 regmatch.rmm_ic = spp->sp_ic;
1987 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001988 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001989 current_lnum,
1990 (colnr_T)lc_col,
Bram Moolenaard23a8232018-02-10 18:45:26 +01001991 IF_SYN_TIME(&spp->sp_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001992 spp->sp_prog = regmatch.regprog;
1993 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001994 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001995 // no match in this line, try another one
Bram Moolenaar071d4272004-06-13 20:20:40 +00001996 spp->sp_startcol = MAXCOL;
1997 continue;
1998 }
1999
2000 /*
2001 * Compute the first column of the match.
2002 */
2003 syn_add_start_off(&pos, &regmatch,
2004 spp, SPO_MS_OFF, -1);
2005 if (pos.lnum > current_lnum)
2006 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002007 // must have used end of match in a next line,
2008 // we can't handle that
Bram Moolenaar071d4272004-06-13 20:20:40 +00002009 spp->sp_startcol = MAXCOL;
2010 continue;
2011 }
2012 startcol = pos.col;
2013
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002014 // remember the next column where this pattern
2015 // matches in the current line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002016 spp->sp_startcol = startcol;
2017
2018 /*
2019 * If a previously found match starts at a lower
2020 * column number, don't use this one.
2021 */
2022 if (startcol >= next_match_col)
2023 continue;
2024
2025 /*
2026 * If we matched this pattern at this position
2027 * before, skip it. Must retry in the next
2028 * column, because it may match from there.
2029 */
2030 if (did_match_already(idx, &zero_width_next_ga))
2031 {
2032 try_next_column = TRUE;
2033 continue;
2034 }
2035
2036 endpos.lnum = regmatch.endpos[0].lnum;
2037 endpos.col = regmatch.endpos[0].col;
2038
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002039 // Compute the highlight start.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002040 syn_add_start_off(&hl_startpos, &regmatch,
2041 spp, SPO_HS_OFF, -1);
2042
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002043 // Compute the region start.
2044 // Default is to use the end of the match.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002045 syn_add_end_off(&eos_pos, &regmatch,
2046 spp, SPO_RS_OFF, 0);
2047
2048 /*
2049 * Grab the external submatches before they get
2050 * overwritten. Reference count doesn't change.
2051 */
2052 unref_extmatch(cur_extmatch);
2053 cur_extmatch = re_extmatch_out;
2054 re_extmatch_out = NULL;
2055
2056 flags = 0;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002057 eoe_pos.lnum = 0; // avoid warning
Bram Moolenaar071d4272004-06-13 20:20:40 +00002058 eoe_pos.col = 0;
2059 end_idx = 0;
2060 hl_endpos.lnum = 0;
2061
2062 /*
2063 * For a "oneline" the end must be found in the
2064 * same line too. Search for it after the end of
2065 * the match with the start pattern. Set the
2066 * resulting end positions at the same time.
2067 */
2068 if (spp->sp_type == SPTYPE_START
2069 && (spp->sp_flags & HL_ONELINE))
2070 {
2071 lpos_T startpos;
2072
2073 startpos = endpos;
2074 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2075 &flags, &eoe_pos, &end_idx, cur_extmatch);
2076 if (endpos.lnum == 0)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002077 continue; // not found
Bram Moolenaar071d4272004-06-13 20:20:40 +00002078 }
2079
2080 /*
2081 * For a "match" the size must be > 0 after the
2082 * end offset needs has been added. Except when
2083 * syncing.
2084 */
2085 else if (spp->sp_type == SPTYPE_MATCH)
2086 {
2087 syn_add_end_off(&hl_endpos, &regmatch, spp,
2088 SPO_HE_OFF, 0);
2089 syn_add_end_off(&endpos, &regmatch, spp,
2090 SPO_ME_OFF, 0);
2091 if (endpos.lnum == current_lnum
2092 && (int)endpos.col + syncing < startcol)
2093 {
2094 /*
2095 * If an empty string is matched, may need
2096 * to try matching again at next column.
2097 */
2098 if (regmatch.startpos[0].col
2099 == regmatch.endpos[0].col)
2100 try_next_column = TRUE;
2101 continue;
2102 }
2103 }
2104
2105 /*
2106 * keep the best match so far in next_match_*
2107 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002108 // Highlighting must start after startpos and end
2109 // before endpos.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002110 if (hl_startpos.lnum == current_lnum
2111 && (int)hl_startpos.col < startcol)
2112 hl_startpos.col = startcol;
2113 limit_pos_zero(&hl_endpos, &endpos);
2114
2115 next_match_idx = idx;
2116 next_match_col = startcol;
2117 next_match_m_endpos = endpos;
2118 next_match_h_endpos = hl_endpos;
2119 next_match_h_startpos = hl_startpos;
2120 next_match_flags = flags;
2121 next_match_eos_pos = eos_pos;
2122 next_match_eoe_pos = eoe_pos;
2123 next_match_end_idx = end_idx;
2124 unref_extmatch(next_match_extmatch);
2125 next_match_extmatch = cur_extmatch;
2126 cur_extmatch = NULL;
2127 }
2128 }
2129 }
2130
2131 /*
2132 * If we found a match at the current column, use it.
2133 */
2134 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2135 {
2136 synpat_T *lspp;
2137
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002138 // When a zero-width item matched which has a nextgroup,
2139 // don't push the item but set nextgroup.
Bram Moolenaar860cae12010-06-05 23:22:07 +02002140 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002141 if (next_match_m_endpos.lnum == current_lnum
2142 && next_match_m_endpos.col == current_col
2143 && lspp->sp_next_list != NULL)
2144 {
2145 current_next_list = lspp->sp_next_list;
2146 current_next_flags = lspp->sp_flags;
2147 keep_next_list = TRUE;
2148 zero_width_next_list = TRUE;
2149
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002150 // Add the index to a list, so that we can check
2151 // later that we don't match it again (and cause an
2152 // endless loop).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002153 if (ga_grow(&zero_width_next_ga, 1) == OK)
2154 {
2155 ((int *)(zero_width_next_ga.ga_data))
2156 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002157 }
2158 next_match_idx = -1;
2159 }
2160 else
2161 cur_si = push_next_match(cur_si);
2162 found_match = TRUE;
2163 }
2164 }
2165 }
2166
2167 /*
2168 * Handle searching for nextgroup match.
2169 */
2170 if (current_next_list != NULL && !keep_next_list)
2171 {
2172 /*
2173 * If a nextgroup was not found, continue looking for one if:
2174 * - this is an empty line and the "skipempty" option was given
2175 * - we are on white space and the "skipwhite" option was given
2176 */
2177 if (!found_match)
2178 {
2179 line = syn_getcurline();
2180 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002181 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002182 || ((current_next_flags & HL_SKIPEMPTY)
2183 && *line == NUL))
2184 break;
2185 }
2186
2187 /*
2188 * If a nextgroup was found: Use it, and continue looking for
2189 * contained matches.
2190 * If a nextgroup was not found: Continue looking for a normal
2191 * match.
2192 * When did set current_next_list for a zero-width item and no
2193 * match was found don't loop (would get stuck).
2194 */
2195 current_next_list = NULL;
2196 next_match_idx = -1;
2197 if (!zero_width_next_list)
2198 found_match = TRUE;
2199 }
2200
2201 } while (found_match);
2202
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002203 restore_chartab(buf_chartab);
2204
Bram Moolenaar071d4272004-06-13 20:20:40 +00002205 /*
2206 * Use attributes from the current state, if within its highlighting.
2207 * If not, use attributes from the current-but-one state, etc.
2208 */
2209 current_attr = 0;
2210#ifdef FEAT_EVAL
2211 current_id = 0;
2212 current_trans_id = 0;
2213#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002214#ifdef FEAT_CONCEAL
2215 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02002216 current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002217#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002218 if (cur_si != NULL)
2219 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002220#ifndef FEAT_EVAL
2221 int current_trans_id = 0;
2222#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002223 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2224 {
2225 sip = &CUR_STATE(idx);
2226 if ((current_lnum > sip->si_h_startpos.lnum
2227 || (current_lnum == sip->si_h_startpos.lnum
2228 && current_col >= sip->si_h_startpos.col))
2229 && (sip->si_h_endpos.lnum == 0
2230 || current_lnum < sip->si_h_endpos.lnum
2231 || (current_lnum == sip->si_h_endpos.lnum
2232 && current_col < sip->si_h_endpos.col)))
2233 {
2234 current_attr = sip->si_attr;
2235#ifdef FEAT_EVAL
2236 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002237#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002238 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002239#ifdef FEAT_CONCEAL
2240 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002241 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002242 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002243#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002244 break;
2245 }
2246 }
2247
Bram Moolenaar217ad922005-03-20 22:37:15 +00002248 if (can_spell != NULL)
2249 {
2250 struct sp_syn sps;
2251
2252 /*
2253 * set "can_spell" to TRUE if spell checking is supposed to be
2254 * done in the current item.
2255 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002256 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002257 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002258 // There is no @Spell cluster: Do spelling for items without
2259 // @NoSpell cluster.
Bram Moolenaar860cae12010-06-05 23:22:07 +02002260 if (syn_block->b_nospell_cluster_id == 0
2261 || current_trans_id == 0)
2262 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002263 else
2264 {
2265 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002266 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002267 sps.cont_in_list = NULL;
2268 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2269 }
2270 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002271 else
2272 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002273 // The @Spell cluster is defined: Do spelling in items with
2274 // the @Spell cluster. But not when @NoSpell is also there.
2275 // At the toplevel only spell check when ":syn spell toplevel"
2276 // was used.
Bram Moolenaar3638c682005-06-08 22:05:14 +00002277 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002278 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002279 else
2280 {
2281 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002282 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002283 sps.cont_in_list = NULL;
2284 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2285
Bram Moolenaar860cae12010-06-05 23:22:07 +02002286 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002287 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002288 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002289 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2290 *can_spell = FALSE;
2291 }
2292 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002293 }
2294 }
2295
2296
Bram Moolenaar071d4272004-06-13 20:20:40 +00002297 /*
2298 * Check for end of current state (and the states before it) at the
2299 * next column. Don't do this for syncing, because we would miss a
2300 * single character match.
2301 * First check if the current state ends at the current column. It
2302 * may be for an empty match and a containing item might end in the
2303 * current column.
2304 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002305 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002306 {
2307 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002308 if (current_state.ga_len > 0
2309 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002310 {
2311 ++current_col;
2312 check_state_ends();
2313 --current_col;
2314 }
2315 }
2316 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002317 else if (can_spell != NULL)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002318 // Default: Only do spelling when there is no @Spell cluster or when
2319 // ":syn spell toplevel" was used.
Bram Moolenaar860cae12010-06-05 23:22:07 +02002320 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2321 ? (syn_block->b_spell_cluster_id == 0)
2322 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002323
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002324 // nextgroup ends at end of line, unless "skipnl" or "skipempty" present
Bram Moolenaar071d4272004-06-13 20:20:40 +00002325 if (current_next_list != NULL
Bram Moolenaar069dafc2018-03-03 20:02:19 +01002326 && (line = syn_getcurline())[current_col] != NUL
2327 && line[current_col + 1] == NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002328 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2329 current_next_list = NULL;
2330
2331 if (zero_width_next_ga.ga_len > 0)
2332 ga_clear(&zero_width_next_ga);
2333
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002334 // No longer need external matches. But keep next_match_extmatch.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002335 unref_extmatch(re_extmatch_out);
2336 re_extmatch_out = NULL;
2337 unref_extmatch(cur_extmatch);
2338
2339 return current_attr;
2340}
2341
2342
2343/*
2344 * Check if we already matched pattern "idx" at the current column.
2345 */
2346 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002347did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002348{
2349 int i;
2350
2351 for (i = current_state.ga_len; --i >= 0; )
2352 if (CUR_STATE(i).si_m_startcol == (int)current_col
2353 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2354 && CUR_STATE(i).si_idx == idx)
2355 return TRUE;
2356
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002357 // Zero-width matches with a nextgroup argument are not put on the syntax
2358 // stack, and can only be matched once anyway.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002359 for (i = gap->ga_len; --i >= 0; )
2360 if (((int *)(gap->ga_data))[i] == idx)
2361 return TRUE;
2362
2363 return FALSE;
2364}
2365
2366/*
2367 * Push the next match onto the stack.
2368 */
2369 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002370push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002371{
2372 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002373#ifdef FEAT_CONCEAL
2374 int save_flags;
2375#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002376
Bram Moolenaar860cae12010-06-05 23:22:07 +02002377 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002378
2379 /*
2380 * Push the item in current_state stack;
2381 */
2382 if (push_current_state(next_match_idx) == OK)
2383 {
2384 /*
2385 * If it's a start-skip-end type that crosses lines, figure out how
2386 * much it continues in this line. Otherwise just fill in the length.
2387 */
2388 cur_si = &CUR_STATE(current_state.ga_len - 1);
2389 cur_si->si_h_startpos = next_match_h_startpos;
2390 cur_si->si_m_startcol = current_col;
2391 cur_si->si_m_lnum = current_lnum;
2392 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002393#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002394 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002395 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002396 if (current_state.ga_len > 1)
2397 cur_si->si_flags |=
2398 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2399#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002400 cur_si->si_next_list = spp->sp_next_list;
2401 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2402 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2403 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002404 // Try to find the end pattern in the current line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002405 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2406 check_keepend();
2407 }
2408 else
2409 {
2410 cur_si->si_m_endpos = next_match_m_endpos;
2411 cur_si->si_h_endpos = next_match_h_endpos;
2412 cur_si->si_ends = TRUE;
2413 cur_si->si_flags |= next_match_flags;
2414 cur_si->si_eoe_pos = next_match_eoe_pos;
2415 cur_si->si_end_idx = next_match_end_idx;
2416 }
2417 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2418 keepend_level = current_state.ga_len - 1;
2419 check_keepend();
2420 update_si_attr(current_state.ga_len - 1);
2421
Bram Moolenaar860cae12010-06-05 23:22:07 +02002422#ifdef FEAT_CONCEAL
2423 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2424#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002425 /*
2426 * If the start pattern has another highlight group, push another item
2427 * on the stack for the start pattern.
2428 */
2429 if ( spp->sp_type == SPTYPE_START
2430 && spp->sp_syn_match_id != 0
2431 && push_current_state(next_match_idx) == OK)
2432 {
2433 cur_si = &CUR_STATE(current_state.ga_len - 1);
2434 cur_si->si_h_startpos = next_match_h_startpos;
2435 cur_si->si_m_startcol = current_col;
2436 cur_si->si_m_lnum = current_lnum;
2437 cur_si->si_m_endpos = next_match_eos_pos;
2438 cur_si->si_h_endpos = next_match_eos_pos;
2439 cur_si->si_ends = TRUE;
2440 cur_si->si_end_idx = 0;
2441 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002442#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002443 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002444 cur_si->si_flags |= save_flags;
2445 if (cur_si->si_flags & HL_CONCEALENDS)
2446 cur_si->si_flags |= HL_CONCEAL;
2447#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002448 cur_si->si_next_list = NULL;
2449 check_keepend();
2450 update_si_attr(current_state.ga_len - 1);
2451 }
2452 }
2453
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002454 next_match_idx = -1; // try other match next time
Bram Moolenaar071d4272004-06-13 20:20:40 +00002455
2456 return cur_si;
2457}
2458
2459/*
2460 * Check for end of current state (and the states before it).
2461 */
2462 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002463check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002464{
2465 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002466 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002467
2468 cur_si = &CUR_STATE(current_state.ga_len - 1);
2469 for (;;)
2470 {
2471 if (cur_si->si_ends
2472 && (cur_si->si_m_endpos.lnum < current_lnum
2473 || (cur_si->si_m_endpos.lnum == current_lnum
2474 && cur_si->si_m_endpos.col <= current_col)))
2475 {
2476 /*
2477 * If there is an end pattern group ID, highlight the end pattern
2478 * now. No need to pop the current item from the stack.
2479 * Only do this if the end pattern continues beyond the current
2480 * position.
2481 */
2482 if (cur_si->si_end_idx
2483 && (cur_si->si_eoe_pos.lnum > current_lnum
2484 || (cur_si->si_eoe_pos.lnum == current_lnum
2485 && cur_si->si_eoe_pos.col > current_col)))
2486 {
2487 cur_si->si_idx = cur_si->si_end_idx;
2488 cur_si->si_end_idx = 0;
2489 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2490 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2491 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002492#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002493 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002494 if (cur_si->si_flags & HL_CONCEALENDS)
2495 cur_si->si_flags |= HL_CONCEAL;
2496#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002497 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002498
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002499 // nextgroup= should not match in the end pattern
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002500 current_next_list = NULL;
2501
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002502 // what matches next may be different now, clear it
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002503 next_match_idx = 0;
2504 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002505 break;
2506 }
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002507
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.
2518 had_extend = (cur_si->si_flags & HL_EXTEND);
2519
2520 pop_current_state();
2521
2522 if (current_state.ga_len == 0)
2523 break;
2524
2525 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002526 {
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002527 syn_update_ends(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002528 if (current_state.ga_len == 0)
2529 break;
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002530 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002531
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002532 cur_si = &CUR_STATE(current_state.ga_len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002533
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002534 /*
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
2544 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
2545 == 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;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002554 }
2555 }
2556 else
2557 break;
2558 }
2559}
2560
2561/*
2562 * Update an entry in the current_state stack for a match or region. This
2563 * fills in si_attr, si_next_list and si_cont_list.
2564 */
2565 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002566update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002567{
2568 stateitem_T *sip = &CUR_STATE(idx);
2569 synpat_T *spp;
2570
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002571 // This should not happen...
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002572 if (sip->si_idx < 0)
2573 return;
2574
Bram Moolenaar860cae12010-06-05 23:22:07 +02002575 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002576 if (sip->si_flags & HL_MATCH)
2577 sip->si_id = spp->sp_syn_match_id;
2578 else
2579 sip->si_id = spp->sp_syn.id;
2580 sip->si_attr = syn_id2attr(sip->si_id);
2581 sip->si_trans_id = sip->si_id;
2582 if (sip->si_flags & HL_MATCH)
2583 sip->si_cont_list = NULL;
2584 else
2585 sip->si_cont_list = spp->sp_cont_list;
2586
2587 /*
2588 * For transparent items, take attr from outer item.
2589 * Also take cont_list, if there is none.
2590 * Don't do this for the matchgroup of a start or end pattern.
2591 */
2592 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2593 {
2594 if (idx == 0)
2595 {
2596 sip->si_attr = 0;
2597 sip->si_trans_id = 0;
2598 if (sip->si_cont_list == NULL)
2599 sip->si_cont_list = ID_LIST_ALL;
2600 }
2601 else
2602 {
2603 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2604 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
2605 if (sip->si_cont_list == NULL)
2606 {
2607 sip->si_flags |= HL_TRANS_CONT;
2608 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2609 }
2610 }
2611 }
2612}
2613
2614/*
2615 * Check the current stack for patterns with "keepend" flag.
2616 * Propagate the match-end to contained items, until a "skipend" item is found.
2617 */
2618 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002619check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002620{
2621 int i;
2622 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002623 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002624 stateitem_T *sip;
2625
2626 /*
2627 * This check can consume a lot of time; only do it from the level where
2628 * there really is a keepend.
2629 */
2630 if (keepend_level < 0)
2631 return;
2632
2633 /*
2634 * Find the last index of an "extend" item. "keepend" items before that
2635 * won't do anything. If there is no "extend" item "i" will be
2636 * "keepend_level" and all "keepend" items will work normally.
2637 */
2638 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2639 if (CUR_STATE(i).si_flags & HL_EXTEND)
2640 break;
2641
2642 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002643 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002644 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002645 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002646 for ( ; i < current_state.ga_len; ++i)
2647 {
2648 sip = &CUR_STATE(i);
2649 if (maxpos.lnum != 0)
2650 {
2651 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002652 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002653 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2654 sip->si_ends = TRUE;
2655 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002656 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2657 {
2658 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002659 || maxpos.lnum > sip->si_m_endpos.lnum
2660 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002661 && maxpos.col > sip->si_m_endpos.col))
2662 maxpos = sip->si_m_endpos;
2663 if (maxpos_h.lnum == 0
2664 || maxpos_h.lnum > sip->si_h_endpos.lnum
2665 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2666 && maxpos_h.col > sip->si_h_endpos.col))
2667 maxpos_h = sip->si_h_endpos;
2668 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002669 }
2670}
2671
2672/*
2673 * Update an entry in the current_state stack for a start-skip-end pattern.
2674 * This finds the end of the current item, if it's in the current line.
2675 *
2676 * Return the flags for the matched END.
2677 */
2678 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002679update_si_end(
2680 stateitem_T *sip,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002681 int startcol, // where to start searching for the end
2682 int force) // when TRUE overrule a previous end
Bram Moolenaar071d4272004-06-13 20:20:40 +00002683{
2684 lpos_T startpos;
2685 lpos_T endpos;
2686 lpos_T hl_endpos;
2687 lpos_T end_endpos;
2688 int end_idx;
2689
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002690 // return quickly for a keyword
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002691 if (sip->si_idx < 0)
2692 return;
2693
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002694 // Don't update when it's already done. Can be a match of an end pattern
2695 // that started in a previous line. Watch out: can also be a "keepend"
2696 // from a containing item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002697 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2698 return;
2699
2700 /*
2701 * We need to find the end of the region. It may continue in the next
2702 * line.
2703 */
2704 end_idx = 0;
2705 startpos.lnum = current_lnum;
2706 startpos.col = startcol;
2707 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2708 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2709
2710 if (endpos.lnum == 0)
2711 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002712 // No end pattern matched.
Bram Moolenaar860cae12010-06-05 23:22:07 +02002713 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002714 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002715 // a "oneline" never continues in the next line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002716 sip->si_ends = TRUE;
2717 sip->si_m_endpos.lnum = current_lnum;
2718 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2719 }
2720 else
2721 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002722 // continues in the next line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002723 sip->si_ends = FALSE;
2724 sip->si_m_endpos.lnum = 0;
2725 }
2726 sip->si_h_endpos = sip->si_m_endpos;
2727 }
2728 else
2729 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002730 // match within this line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002731 sip->si_m_endpos = endpos;
2732 sip->si_h_endpos = hl_endpos;
2733 sip->si_eoe_pos = end_endpos;
2734 sip->si_ends = TRUE;
2735 sip->si_end_idx = end_idx;
2736 }
2737}
2738
2739/*
2740 * Add a new state to the current state stack.
2741 * It is cleared and the index set to "idx".
2742 * Return FAIL if it's not possible (out of memory).
2743 */
2744 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002745push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002746{
2747 if (ga_grow(&current_state, 1) == FAIL)
2748 return FAIL;
Bram Moolenaara80faa82020-04-12 19:37:17 +02002749 CLEAR_POINTER(&CUR_STATE(current_state.ga_len));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002750 CUR_STATE(current_state.ga_len).si_idx = idx;
2751 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002752 return OK;
2753}
2754
2755/*
2756 * Remove a state from the current_state stack.
2757 */
2758 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002759pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002760{
2761 if (current_state.ga_len)
2762 {
2763 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2764 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002765 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002766 // after the end of a pattern, try matching a keyword or pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00002767 next_match_idx = -1;
2768
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002769 // if first state with "keepend" is popped, reset keepend_level
Bram Moolenaar071d4272004-06-13 20:20:40 +00002770 if (keepend_level >= current_state.ga_len)
2771 keepend_level = -1;
2772}
2773
2774/*
2775 * Find the end of a start/skip/end syntax region after "startpos".
2776 * Only checks one line.
2777 * Also handles a match item that continued from a previous line.
2778 * If not found, the syntax item continues in the next line. m_endpos->lnum
2779 * will be 0.
2780 * If found, the end of the region and the end of the highlighting is
2781 * computed.
2782 */
2783 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002784find_endpos(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002785 int idx, // index of the pattern
2786 lpos_T *startpos, // where to start looking for an END match
2787 lpos_T *m_endpos, // return: end of match
2788 lpos_T *hl_endpos, // return: end of highlighting
2789 long *flagsp, // return: flags of matching END
2790 lpos_T *end_endpos, // return: end of end pattern match
2791 int *end_idx, // return: group ID for end pat. match, or 0
2792 reg_extmatch_T *start_ext) // submatches from the start pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00002793{
2794 colnr_T matchcol;
2795 synpat_T *spp, *spp_skip;
2796 int start_idx;
2797 int best_idx;
2798 regmmatch_T regmatch;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002799 regmmatch_T best_regmatch; // startpos/endpos of best match
Bram Moolenaar071d4272004-06-13 20:20:40 +00002800 lpos_T pos;
2801 char_u *line;
2802 int had_match = FALSE;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002803 char_u buf_chartab[32]; // chartab array for syn option iskyeyword
Bram Moolenaar071d4272004-06-13 20:20:40 +00002804
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002805 // just in case we are invoked for a keyword
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002806 if (idx < 0)
2807 return;
2808
Bram Moolenaar071d4272004-06-13 20:20:40 +00002809 /*
2810 * Check for being called with a START pattern.
2811 * Can happen with a match that continues to the next line, because it
2812 * contained a region.
2813 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002814 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002815 if (spp->sp_type != SPTYPE_START)
2816 {
2817 *hl_endpos = *startpos;
2818 return;
2819 }
2820
2821 /*
2822 * Find the SKIP or first END pattern after the last START pattern.
2823 */
2824 for (;;)
2825 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002826 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002827 if (spp->sp_type != SPTYPE_START)
2828 break;
2829 ++idx;
2830 }
2831
2832 /*
2833 * Lookup the SKIP pattern (if present)
2834 */
2835 if (spp->sp_type == SPTYPE_SKIP)
2836 {
2837 spp_skip = spp;
2838 ++idx;
2839 }
2840 else
2841 spp_skip = NULL;
2842
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002843 // Setup external matches for syn_regexec().
Bram Moolenaar071d4272004-06-13 20:20:40 +00002844 unref_extmatch(re_extmatch_in);
2845 re_extmatch_in = ref_extmatch(start_ext);
2846
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002847 matchcol = startpos->col; // start looking for a match at sstart
2848 start_idx = idx; // remember the first END pattern.
2849 best_regmatch.startpos[0].col = 0; // avoid compiler warning
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002850
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002851 // use syntax iskeyword option
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002852 save_chartab(buf_chartab);
2853
Bram Moolenaar071d4272004-06-13 20:20:40 +00002854 for (;;)
2855 {
2856 /*
2857 * Find end pattern that matches first after "matchcol".
2858 */
2859 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002860 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002861 {
2862 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002863 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002864
Bram Moolenaar860cae12010-06-05 23:22:07 +02002865 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002866 if (spp->sp_type != SPTYPE_END) // past last END pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00002867 break;
2868 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2869 if (lc_col < 0)
2870 lc_col = 0;
2871
2872 regmatch.rmm_ic = spp->sp_ic;
2873 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002874 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
2875 IF_SYN_TIME(&spp->sp_time));
2876 spp->sp_prog = regmatch.regprog;
2877 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002878 {
2879 if (best_idx == -1 || regmatch.startpos[0].col
2880 < best_regmatch.startpos[0].col)
2881 {
2882 best_idx = idx;
2883 best_regmatch.startpos[0] = regmatch.startpos[0];
2884 best_regmatch.endpos[0] = regmatch.endpos[0];
2885 }
2886 }
2887 }
2888
2889 /*
2890 * If all end patterns have been tried, and there is no match, the
2891 * item continues until end-of-line.
2892 */
2893 if (best_idx == -1)
2894 break;
2895
2896 /*
2897 * If the skip pattern matches before the end pattern,
2898 * continue searching after the skip pattern.
2899 */
2900 if (spp_skip != NULL)
2901 {
2902 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002903 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002904
2905 if (lc_col < 0)
2906 lc_col = 0;
2907 regmatch.rmm_ic = spp_skip->sp_ic;
2908 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002909 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
2910 IF_SYN_TIME(&spp_skip->sp_time));
2911 spp_skip->sp_prog = regmatch.regprog;
2912 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00002913 <= best_regmatch.startpos[0].col)
2914 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01002915 int line_len;
2916
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002917 // Add offset to skip pattern match
Bram Moolenaar071d4272004-06-13 20:20:40 +00002918 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2919
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002920 // If the skip pattern goes on to the next line, there is no
2921 // match with an end pattern in this line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002922 if (pos.lnum > startpos->lnum)
2923 break;
2924
2925 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01002926 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002927
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002928 // take care of an empty match or negative offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00002929 if (pos.col <= matchcol)
2930 ++matchcol;
2931 else if (pos.col <= regmatch.endpos[0].col)
2932 matchcol = pos.col;
2933 else
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002934 // Be careful not to jump over the NUL at the end-of-line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002935 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01002936 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002937 ++matchcol)
2938 ;
2939
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002940 // if the skip pattern includes end-of-line, break here
Bram Moolenaar04bff882016-01-05 20:46:16 +01002941 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002942 break;
2943
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002944 continue; // start with first end pattern again
Bram Moolenaar071d4272004-06-13 20:20:40 +00002945 }
2946 }
2947
2948 /*
2949 * Match from start pattern to end pattern.
2950 * Correct for match and highlight offset of end pattern.
2951 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002952 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002953 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002954 // can't end before the start
Bram Moolenaar071d4272004-06-13 20:20:40 +00002955 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
2956 m_endpos->col = startpos->col;
2957
2958 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002959 // can't end before the start
Bram Moolenaar071d4272004-06-13 20:20:40 +00002960 if (end_endpos->lnum == startpos->lnum
2961 && end_endpos->col < startpos->col)
2962 end_endpos->col = startpos->col;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002963 // can't end after the match
Bram Moolenaar071d4272004-06-13 20:20:40 +00002964 limit_pos(end_endpos, m_endpos);
2965
2966 /*
2967 * If the end group is highlighted differently, adjust the pointers.
2968 */
2969 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
2970 {
2971 *end_idx = best_idx;
2972 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
2973 {
2974 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
2975 hl_endpos->col = best_regmatch.endpos[0].col;
2976 }
2977 else
2978 {
2979 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
2980 hl_endpos->col = best_regmatch.startpos[0].col;
2981 }
2982 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
2983
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002984 // can't end before the start
Bram Moolenaar071d4272004-06-13 20:20:40 +00002985 if (hl_endpos->lnum == startpos->lnum
2986 && hl_endpos->col < startpos->col)
2987 hl_endpos->col = startpos->col;
2988 limit_pos(hl_endpos, m_endpos);
2989
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002990 // now the match ends where the highlighting ends, it is turned
2991 // into the matchgroup for the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00002992 *m_endpos = *hl_endpos;
2993 }
2994 else
2995 {
2996 *end_idx = 0;
2997 *hl_endpos = *end_endpos;
2998 }
2999
3000 *flagsp = spp->sp_flags;
3001
3002 had_match = TRUE;
3003 break;
3004 }
3005
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003006 // no match for an END pattern in this line
Bram Moolenaar071d4272004-06-13 20:20:40 +00003007 if (!had_match)
3008 m_endpos->lnum = 0;
3009
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003010 restore_chartab(buf_chartab);
3011
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003012 // Remove external matches.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003013 unref_extmatch(re_extmatch_in);
3014 re_extmatch_in = NULL;
3015}
3016
3017/*
3018 * Limit "pos" not to be after "limit".
3019 */
3020 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003021limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003022{
3023 if (pos->lnum > limit->lnum)
3024 *pos = *limit;
3025 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3026 pos->col = limit->col;
3027}
3028
3029/*
3030 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3031 */
3032 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003033limit_pos_zero(
3034 lpos_T *pos,
3035 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003036{
3037 if (pos->lnum == 0)
3038 *pos = *limit;
3039 else
3040 limit_pos(pos, limit);
3041}
3042
3043/*
3044 * Add offset to matched text for end of match or highlight.
3045 */
3046 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003047syn_add_end_off(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003048 lpos_T *result, // returned position
3049 regmmatch_T *regmatch, // start/end of match
3050 synpat_T *spp, // matched pattern
3051 int idx, // index of offset
3052 int extra) // extra chars for offset to start
Bram Moolenaar071d4272004-06-13 20:20:40 +00003053{
3054 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003055 int off;
3056 char_u *base;
3057 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003058
3059 if (spp->sp_off_flags & (1 << idx))
3060 {
3061 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003062 col = regmatch->startpos[0].col;
3063 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003064 }
3065 else
3066 {
3067 result->lnum = regmatch->endpos[0].lnum;
3068 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003069 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003070 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003071 // Don't go past the end of the line. Matters for "rs=e+2" when there
3072 // is a matchgroup. Watch out for match with last NL in the buffer.
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003073 if (result->lnum > syn_buf->b_ml.ml_line_count)
3074 col = 0;
3075 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003076 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003077 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3078 p = base + col;
3079 if (off > 0)
3080 {
3081 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003082 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003083 }
3084 else if (off < 0)
3085 {
3086 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003087 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003088 }
3089 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003090 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003091 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003092}
3093
3094/*
3095 * Add offset to matched text for start of match or highlight.
3096 * Avoid resulting column to become negative.
3097 */
3098 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003099syn_add_start_off(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003100 lpos_T *result, // returned position
3101 regmmatch_T *regmatch, // start/end of match
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003102 synpat_T *spp,
3103 int idx,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003104 int extra) // extra chars for offset to end
Bram Moolenaar071d4272004-06-13 20:20:40 +00003105{
3106 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003107 int off;
3108 char_u *base;
3109 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003110
3111 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3112 {
3113 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003114 col = regmatch->endpos[0].col;
3115 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003116 }
3117 else
3118 {
3119 result->lnum = regmatch->startpos[0].lnum;
3120 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003121 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003122 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003123 if (result->lnum > syn_buf->b_ml.ml_line_count)
3124 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003125 // a "\n" at the end of the pattern may take us below the last line
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003126 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003127 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003128 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003129 if (off != 0)
3130 {
3131 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3132 p = base + col;
3133 if (off > 0)
3134 {
3135 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003136 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003137 }
3138 else if (off < 0)
3139 {
3140 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003141 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003142 }
3143 col = (int)(p - base);
3144 }
3145 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003146}
3147
3148/*
3149 * Get current line in syntax buffer.
3150 */
3151 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003152syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003153{
3154 return ml_get_buf(syn_buf, current_lnum, FALSE);
3155}
3156
3157/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003158 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003159 * Returns TRUE when there is a match.
3160 */
3161 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003162syn_regexec(
3163 regmmatch_T *rmp,
3164 linenr_T lnum,
3165 colnr_T col,
3166 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003167{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003168 int r;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003169#ifdef FEAT_RELTIME
3170 int timed_out = FALSE;
3171#endif
Bram Moolenaarf7512552013-06-06 14:55:19 +02003172#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003173 proftime_T pt;
3174
3175 if (syn_time_on)
3176 profile_start(&pt);
3177#endif
3178
Bram Moolenaarbcf94422018-06-23 14:21:42 +02003179 if (rmp->regprog == NULL)
3180 // This can happen if a previous call to vim_regexec_multi() tried to
3181 // use the NFA engine, which resulted in NFA_TOO_EXPENSIVE, and
3182 // compiling the pattern with the other engine fails.
3183 return FALSE;
3184
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003185 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003186 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
3187#ifdef FEAT_RELTIME
3188 syn_tm, &timed_out
3189#else
3190 NULL, NULL
3191#endif
3192 );
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003193
Bram Moolenaarf7512552013-06-06 14:55:19 +02003194#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003195 if (syn_time_on)
3196 {
3197 profile_end(&pt);
3198 profile_add(&st->total, &pt);
3199 if (profile_cmp(&pt, &st->slowest) < 0)
3200 st->slowest = pt;
3201 ++st->count;
3202 if (r > 0)
3203 ++st->match;
3204 }
3205#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003206#ifdef FEAT_RELTIME
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003207 if (timed_out && !syn_win->w_s->b_syn_slow)
3208 {
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003209 syn_win->w_s->b_syn_slow = TRUE;
Bram Moolenaar32526b32019-01-19 17:43:09 +01003210 msg(_("'redrawtime' exceeded, syntax highlighting disabled"));
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003211 }
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003212#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003213
3214 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003215 {
3216 rmp->startpos[0].lnum += lnum;
3217 rmp->endpos[0].lnum += lnum;
3218 return TRUE;
3219 }
3220 return FALSE;
3221}
3222
3223/*
3224 * Check one position in a line for a matching keyword.
3225 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003226 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003227 */
3228 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003229check_keyword_id(
3230 char_u *line,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003231 int startcol, // position in line to check for keyword
3232 int *endcolp, // return: character after found keyword
3233 long *flagsp, // return: flags of matching keyword
3234 short **next_listp, // return: next_list of matching keyword
3235 stateitem_T *cur_si, // item at the top of the stack
3236 int *ccharp UNUSED) // conceal substitution char
Bram Moolenaar071d4272004-06-13 20:20:40 +00003237{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003238 keyentry_T *kp;
3239 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003240 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003241 int kwlen;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003242 char_u keyword[MAXKEYWLEN + 1]; // assume max. keyword len is 80
Bram Moolenaardad6b692005-01-25 22:14:34 +00003243 hashtab_T *ht;
3244 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003245
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003246 // Find first character after the keyword. First character was already
3247 // checked.
Bram Moolenaardad6b692005-01-25 22:14:34 +00003248 kwp = line + startcol;
3249 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003250 do
3251 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003252 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003253 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003254 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00003255 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003256 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003257 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003258
Bram Moolenaardad6b692005-01-25 22:14:34 +00003259 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003260 return 0;
3261
3262 /*
3263 * Must make a copy of the keyword, so we can add a NUL and make it
3264 * lowercase.
3265 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003266 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003267
3268 /*
3269 * Try twice:
3270 * 1. matching case
3271 * 2. ignoring case
3272 */
3273 for (round = 1; round <= 2; ++round)
3274 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003275 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003276 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003277 continue;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003278 if (round == 2) // ignore case
Bram Moolenaardad6b692005-01-25 22:14:34 +00003279 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003280
3281 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003282 * Find keywords that match. There can be several with different
3283 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003284 * When current_next_list is non-zero accept only that group, otherwise:
3285 * Accept a not-contained keyword at toplevel.
3286 * Accept a keyword at other levels only if it is in the contains list.
3287 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003288 hi = hash_find(ht, keyword);
3289 if (!HASHITEM_EMPTY(hi))
3290 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003291 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003292 if (current_next_list != 0
3293 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3294 : (cur_si == NULL
3295 ? !(kp->flags & HL_CONTAINED)
3296 : in_id_list(cur_si, cur_si->si_cont_list,
3297 &kp->k_syn, kp->flags & HL_CONTAINED)))
3298 {
3299 *endcolp = startcol + kwlen;
3300 *flagsp = kp->flags;
3301 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003302#ifdef FEAT_CONCEAL
3303 *ccharp = kp->k_char;
3304#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003305 return kp->k_syn.id;
3306 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003307 }
3308 }
3309 return 0;
3310}
3311
3312/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003313 * Handle ":syntax conceal" command.
3314 */
3315 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003316syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003317{
3318#ifdef FEAT_CONCEAL
3319 char_u *arg = eap->arg;
3320 char_u *next;
3321
3322 eap->nextcmd = find_nextcmd(arg);
3323 if (eap->skip)
3324 return;
3325
3326 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003327 if (*arg == NUL)
3328 {
3329 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003330 msg("syntax conceal on");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003331 else
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003332 msg("syntax conceal off");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003333 }
3334 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003335 curwin->w_s->b_syn_conceal = TRUE;
3336 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3337 curwin->w_s->b_syn_conceal = FALSE;
3338 else
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003339 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003340#endif
3341}
3342
3343/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003344 * Handle ":syntax case" command.
3345 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003346 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003347syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003348{
3349 char_u *arg = eap->arg;
3350 char_u *next;
3351
3352 eap->nextcmd = find_nextcmd(arg);
3353 if (eap->skip)
3354 return;
3355
3356 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003357 if (*arg == NUL)
3358 {
3359 if (curwin->w_s->b_syn_ic)
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003360 msg("syntax case ignore");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003361 else
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003362 msg("syntax case match");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003363 }
3364 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003365 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003366 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003367 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003368 else
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003369 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003370}
3371
3372/*
3373 * Handle ":syntax foldlevel" command.
3374 */
3375 static void
3376syn_cmd_foldlevel(exarg_T *eap, int syncing UNUSED)
3377{
3378 char_u *arg = eap->arg;
3379 char_u *arg_end;
3380
3381 eap->nextcmd = find_nextcmd(arg);
3382 if (eap->skip)
3383 return;
3384
3385 if (*arg == NUL)
3386 {
3387 switch (curwin->w_s->b_syn_foldlevel)
3388 {
Dominique Pellecd53eed2022-02-05 18:53:06 +00003389 case SYNFLD_START: msg("syntax foldlevel start"); break;
3390 case SYNFLD_MINIMUM: msg("syntax foldlevel minimum"); break;
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003391 default: break;
3392 }
3393 return;
3394 }
3395
3396 arg_end = skiptowhite(arg);
3397 if (STRNICMP(arg, "start", 5) == 0 && arg_end - arg == 5)
3398 curwin->w_s->b_syn_foldlevel = SYNFLD_START;
3399 else if (STRNICMP(arg, "minimum", 7) == 0 && arg_end - arg == 7)
3400 curwin->w_s->b_syn_foldlevel = SYNFLD_MINIMUM;
3401 else
3402 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003403 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003404 return;
3405 }
3406
3407 arg = skipwhite(arg_end);
3408 if (*arg != NUL)
3409 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003410 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003411 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003412}
3413
3414/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003415 * Handle ":syntax spell" command.
3416 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003417 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003418syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003419{
3420 char_u *arg = eap->arg;
3421 char_u *next;
3422
3423 eap->nextcmd = find_nextcmd(arg);
3424 if (eap->skip)
3425 return;
3426
3427 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003428 if (*arg == NUL)
3429 {
3430 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
Dominique Pellecd53eed2022-02-05 18:53:06 +00003431 msg("syntax spell toplevel");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003432 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
Dominique Pellecd53eed2022-02-05 18:53:06 +00003433 msg("syntax spell notoplevel");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003434 else
Dominique Pellecd53eed2022-02-05 18:53:06 +00003435 msg("syntax spell default");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003436 }
3437 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003438 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003439 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003440 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003441 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003442 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003443 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003444 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003445 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003446 return;
3447 }
3448
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003449 // assume spell checking changed, force a redraw
Bram Moolenaar5081d202015-06-25 18:36:26 +02003450 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003451}
3452
3453/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003454 * Handle ":syntax iskeyword" command.
3455 */
3456 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003457syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003458{
3459 char_u *arg = eap->arg;
3460 char_u save_chartab[32];
3461 char_u *save_isk;
3462
3463 if (eap->skip)
3464 return;
3465
3466 arg = skipwhite(arg);
3467 if (*arg == NUL)
3468 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003469 msg_puts("\n");
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003470 if (curwin->w_s->b_syn_isk != empty_option)
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003471 {
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003472 msg_puts("syntax iskeyword ");
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003473 msg_outtrans(curwin->w_s->b_syn_isk);
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003474 }
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003475 else
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003476 msg_outtrans((char_u *)_("syntax iskeyword not set"));
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003477 }
3478 else
3479 {
3480 if (STRNICMP(arg, "clear", 5) == 0)
3481 {
3482 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3483 (size_t)32);
3484 clear_string_option(&curwin->w_s->b_syn_isk);
3485 }
3486 else
3487 {
3488 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3489 save_isk = curbuf->b_p_isk;
3490 curbuf->b_p_isk = vim_strsave(arg);
3491
3492 buf_init_chartab(curbuf, FALSE);
3493 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3494 (size_t)32);
3495 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3496 clear_string_option(&curwin->w_s->b_syn_isk);
3497 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3498 curbuf->b_p_isk = save_isk;
3499 }
3500 }
3501 redraw_win_later(curwin, NOT_VALID);
3502}
3503
3504/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003505 * Clear all syntax info for one buffer.
3506 */
3507 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003508syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003509{
3510 int i;
3511
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003512 block->b_syn_error = FALSE; // clear previous error
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003513#ifdef FEAT_RELTIME
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003514 block->b_syn_slow = FALSE; // clear previous timeout
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003515#endif
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003516 block->b_syn_ic = FALSE; // Use case, by default
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003517 block->b_syn_foldlevel = SYNFLD_START;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003518 block->b_syn_spell = SYNSPL_DEFAULT; // default spell checking
Bram Moolenaar860cae12010-06-05 23:22:07 +02003519 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003520#ifdef FEAT_CONCEAL
3521 block->b_syn_conceal = FALSE;
3522#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003523
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003524 // free the keywords
Bram Moolenaar860cae12010-06-05 23:22:07 +02003525 clear_keywtab(&block->b_keywtab);
3526 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003527
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003528 // free the syntax patterns
Bram Moolenaar860cae12010-06-05 23:22:07 +02003529 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3530 syn_clear_pattern(block, i);
3531 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003532
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003533 // free the syntax clusters
Bram Moolenaar860cae12010-06-05 23:22:07 +02003534 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3535 syn_clear_cluster(block, i);
3536 ga_clear(&block->b_syn_clusters);
3537 block->b_spell_cluster_id = 0;
3538 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003539
Bram Moolenaar860cae12010-06-05 23:22:07 +02003540 block->b_syn_sync_flags = 0;
3541 block->b_syn_sync_minlines = 0;
3542 block->b_syn_sync_maxlines = 0;
3543 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003544
Bram Moolenaar473de612013-06-08 18:19:48 +02003545 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003546 block->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003547 VIM_CLEAR(block->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003548#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003549 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003550#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003551 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003552
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003553 // free the stored states
Bram Moolenaar860cae12010-06-05 23:22:07 +02003554 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003555 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003556
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003557 // Reset the counter for ":syn include"
Bram Moolenaar42431a72011-04-01 14:44:59 +02003558 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003559}
3560
3561/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003562 * Get rid of ownsyntax for window "wp".
3563 */
3564 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003565reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003566{
3567 if (wp->w_s != &wp->w_buffer->b_s)
3568 {
3569 syntax_clear(wp->w_s);
3570 vim_free(wp->w_s);
3571 wp->w_s = &wp->w_buffer->b_s;
3572 }
3573}
3574
3575/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003576 * Clear syncing info for one buffer.
3577 */
3578 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003579syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003580{
3581 int i;
3582
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003583 // free the syntax patterns
Bram Moolenaar860cae12010-06-05 23:22:07 +02003584 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3585 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3586 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003587
Bram Moolenaar860cae12010-06-05 23:22:07 +02003588 curwin->w_s->b_syn_sync_flags = 0;
3589 curwin->w_s->b_syn_sync_minlines = 0;
3590 curwin->w_s->b_syn_sync_maxlines = 0;
3591 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003592
Bram Moolenaar473de612013-06-08 18:19:48 +02003593 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003594 curwin->w_s->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003595 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003596 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003597
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003598 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003599}
3600
3601/*
3602 * Remove one pattern from the buffer's pattern list.
3603 */
3604 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003605syn_remove_pattern(
3606 synblock_T *block,
3607 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003608{
3609 synpat_T *spp;
3610
Bram Moolenaar860cae12010-06-05 23:22:07 +02003611 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003612#ifdef FEAT_FOLDING
3613 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003614 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003615#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003616 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003617 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003618 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3619 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003620}
3621
3622/*
3623 * Clear and free one syntax pattern. When clearing all, must be called from
3624 * last to first!
3625 */
3626 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003627syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003628{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003629 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003630 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003631 // Only free sp_cont_list and sp_next_list of first start pattern
Bram Moolenaar860cae12010-06-05 23:22:07 +02003632 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003633 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003634 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3635 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3636 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003637 }
3638}
3639
3640/*
3641 * Clear and free one syntax cluster.
3642 */
3643 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003644syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003645{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003646 vim_free(SYN_CLSTR(block)[i].scl_name);
3647 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3648 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003649}
3650
3651/*
3652 * Handle ":syntax clear" command.
3653 */
3654 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003655syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003656{
3657 char_u *arg = eap->arg;
3658 char_u *arg_end;
3659 int id;
3660
3661 eap->nextcmd = find_nextcmd(arg);
3662 if (eap->skip)
3663 return;
3664
3665 /*
3666 * We have to disable this within ":syn include @group filename",
3667 * because otherwise @group would get deleted.
3668 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3669 * clear".
3670 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003671 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003672 return;
3673
Bram Moolenaar1966c242020-04-20 22:42:32 +02003674 if (ends_excmd2(eap->cmd, arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003675 {
3676 /*
3677 * No argument: Clear all syntax items.
3678 */
3679 if (syncing)
3680 syntax_sync_clear();
3681 else
3682 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003683 syntax_clear(curwin->w_s);
3684 if (curwin->w_s == &curwin->w_buffer->b_s)
3685 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003686 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003687 }
3688 }
3689 else
3690 {
3691 /*
3692 * Clear the group IDs that are in the argument.
3693 */
Bram Moolenaar1966c242020-04-20 22:42:32 +02003694 while (!ends_excmd2(eap->cmd, arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003695 {
3696 arg_end = skiptowhite(arg);
3697 if (*arg == '@')
3698 {
3699 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3700 if (id == 0)
3701 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003702 semsg(_(e_no_such_syntax_cluster_1), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003703 break;
3704 }
3705 else
3706 {
3707 /*
3708 * We can't physically delete a cluster without changing
3709 * the IDs of other clusters, so we do the next best thing
3710 * and make it empty.
3711 */
3712 short scl_id = id - SYNID_CLUSTER;
3713
Bram Moolenaard23a8232018-02-10 18:45:26 +01003714 VIM_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003715 }
3716 }
3717 else
3718 {
3719 id = syn_namen2id(arg, (int)(arg_end - arg));
3720 if (id == 0)
3721 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +02003722 semsg(_(e_no_such_highlight_group_name_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003723 break;
3724 }
3725 else
3726 syn_clear_one(id, syncing);
3727 }
3728 arg = skipwhite(arg_end);
3729 }
3730 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003731 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003732 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003733}
3734
3735/*
3736 * Clear one syntax group for the current buffer.
3737 */
3738 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003739syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003740{
3741 synpat_T *spp;
3742 int idx;
3743
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003744 // Clear keywords only when not ":syn sync clear group-name"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003745 if (!syncing)
3746 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003747 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3748 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003749 }
3750
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003751 // clear the patterns for "id"
Bram Moolenaar860cae12010-06-05 23:22:07 +02003752 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003753 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003754 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003755 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3756 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003757 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003758 }
3759}
3760
3761/*
3762 * Handle ":syntax on" command.
3763 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003764 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003765syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003766{
3767 syn_cmd_onoff(eap, "syntax");
3768}
3769
3770/*
3771 * Handle ":syntax enable" command.
3772 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003773 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003774syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003775{
Bram Moolenaarc8a9fe52021-11-20 19:50:59 +00003776 set_internal_string_var((char_u *)"g:syntax_cmd", (char_u *)"enable");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003777 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003778 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003779}
3780
3781/*
3782 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003783 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003784 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003785 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003786syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003787{
Bram Moolenaar63b91732021-08-05 20:40:03 +02003788 set_nextcmd(eap, eap->arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003789 if (!eap->skip)
3790 {
Bram Moolenaarc8a9fe52021-11-20 19:50:59 +00003791 set_internal_string_var((char_u *)"g:syntax_cmd", (char_u *)"reset");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003792 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003793 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003794 }
3795}
3796
3797/*
3798 * Handle ":syntax manual" command.
3799 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003800 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003801syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003802{
3803 syn_cmd_onoff(eap, "manual");
3804}
3805
3806/*
3807 * Handle ":syntax off" command.
3808 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003809 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003810syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003811{
3812 syn_cmd_onoff(eap, "nosyntax");
3813}
3814
3815 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003816syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003817{
3818 char_u buf[100];
3819
Bram Moolenaar63b91732021-08-05 20:40:03 +02003820 set_nextcmd(eap, eap->arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003821 if (!eap->skip)
3822 {
3823 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003824 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003825 do_cmdline_cmd(buf);
3826 }
3827}
3828
3829/*
3830 * Handle ":syntax [list]" command: list current syntax words.
3831 */
3832 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003833syn_cmd_list(
3834 exarg_T *eap,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003835 int syncing) // when TRUE: list syncing items
Bram Moolenaar071d4272004-06-13 20:20:40 +00003836{
3837 char_u *arg = eap->arg;
3838 int id;
3839 char_u *arg_end;
3840
3841 eap->nextcmd = find_nextcmd(arg);
3842 if (eap->skip)
3843 return;
3844
Bram Moolenaar860cae12010-06-05 23:22:07 +02003845 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003846 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003847 msg(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003848 return;
3849 }
3850
3851 if (syncing)
3852 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003853 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003854 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003855 msg_puts(_("syncing on C-style comments"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003856 syn_lines_msg();
3857 syn_match_msg();
3858 return;
3859 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003860 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003861 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003862 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003863 msg_puts(_("no syncing"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003864 else
3865 {
Bram Moolenaar99502802020-11-18 16:53:23 +01003866 if (curwin->w_s->b_syn_sync_minlines == MAXLNUM)
3867 msg_puts(_("syncing starts at the first line"));
3868 else
3869 {
3870 msg_puts(_("syncing starts "));
3871 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3872 msg_puts(_(" lines before top line"));
3873 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003874 syn_match_msg();
3875 }
3876 return;
3877 }
Bram Moolenaar32526b32019-01-19 17:43:09 +01003878 msg_puts_title(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003879 if (curwin->w_s->b_syn_sync_minlines > 0
3880 || curwin->w_s->b_syn_sync_maxlines > 0
3881 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003882 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003883 msg_puts(_("\nsyncing on items"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003884 syn_lines_msg();
3885 syn_match_msg();
3886 }
3887 }
3888 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01003889 msg_puts_title(_("\n--- Syntax items ---"));
Bram Moolenaar1966c242020-04-20 22:42:32 +02003890 if (ends_excmd2(eap->cmd, arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003891 {
3892 /*
3893 * No argument: List all group IDs and all syntax clusters.
3894 */
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02003895 for (id = 1; id <= highlight_num_groups() && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003896 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003897 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003898 syn_list_cluster(id);
3899 }
3900 else
3901 {
3902 /*
3903 * List the group IDs and syntax clusters that are in the argument.
3904 */
Bram Moolenaar1966c242020-04-20 22:42:32 +02003905 while (!ends_excmd2(eap->cmd, arg) && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003906 {
3907 arg_end = skiptowhite(arg);
3908 if (*arg == '@')
3909 {
3910 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3911 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003912 semsg(_(e_no_such_syntax_cluster_2), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003913 else
3914 syn_list_cluster(id - SYNID_CLUSTER);
3915 }
3916 else
3917 {
3918 id = syn_namen2id(arg, (int)(arg_end - arg));
3919 if (id == 0)
Bram Moolenaare29a27f2021-07-20 21:07:36 +02003920 semsg(_(e_no_such_highlight_group_name_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003921 else
3922 syn_list_one(id, syncing, TRUE);
3923 }
3924 arg = skipwhite(arg_end);
3925 }
3926 }
Bram Moolenaar63b91732021-08-05 20:40:03 +02003927 set_nextcmd(eap, arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003928}
3929
3930 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003931syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003932{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003933 if (curwin->w_s->b_syn_sync_maxlines > 0
3934 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003935 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003936 msg_puts("; ");
Bram Moolenaar99502802020-11-18 16:53:23 +01003937 if (curwin->w_s->b_syn_sync_minlines == MAXLNUM)
3938 msg_puts(_("from the first line"));
3939 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003940 {
Bram Moolenaar99502802020-11-18 16:53:23 +01003941 if (curwin->w_s->b_syn_sync_minlines > 0)
3942 {
3943 msg_puts(_("minimal "));
3944 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3945 if (curwin->w_s->b_syn_sync_maxlines)
3946 msg_puts(", ");
3947 }
3948 if (curwin->w_s->b_syn_sync_maxlines > 0)
3949 {
3950 msg_puts(_("maximal "));
3951 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
3952 }
3953 msg_puts(_(" lines before top line"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003954 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003955 }
3956}
3957
3958 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003959syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003960{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003961 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003962 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003963 msg_puts(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003964 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar32526b32019-01-19 17:43:09 +01003965 msg_puts(_(" line breaks"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003966 }
3967}
3968
3969static int last_matchgroup;
3970
3971struct name_list
3972{
3973 int flag;
3974 char *name;
3975};
3976
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01003977static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003978
3979/*
3980 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3981 */
3982 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003983syn_list_one(
3984 int id,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003985 int syncing, // when TRUE: list syncing items
3986 int link_only) // when TRUE; list link-only too
Bram Moolenaar071d4272004-06-13 20:20:40 +00003987{
3988 int attr;
3989 int idx;
3990 int did_header = FALSE;
3991 synpat_T *spp;
3992 static struct name_list namelist1[] =
3993 {
3994 {HL_DISPLAY, "display"},
3995 {HL_CONTAINED, "contained"},
3996 {HL_ONELINE, "oneline"},
3997 {HL_KEEPEND, "keepend"},
3998 {HL_EXTEND, "extend"},
3999 {HL_EXCLUDENL, "excludenl"},
4000 {HL_TRANSP, "transparent"},
4001 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004002#ifdef FEAT_CONCEAL
4003 {HL_CONCEAL, "conceal"},
4004 {HL_CONCEALENDS, "concealends"},
4005#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004006 {0, NULL}
4007 };
4008 static struct name_list namelist2[] =
4009 {
4010 {HL_SKIPWHITE, "skipwhite"},
4011 {HL_SKIPNL, "skipnl"},
4012 {HL_SKIPEMPTY, "skipempty"},
4013 {0, NULL}
4014 };
4015
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004016 attr = HL_ATTR(HLF_D); // highlight like directories
Bram Moolenaar071d4272004-06-13 20:20:40 +00004017
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004018 // list the keywords for "id"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004019 if (!syncing)
4020 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004021 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4022 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004023 did_header, attr);
4024 }
4025
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004026 // list the patterns for "id"
Bram Moolenaar860cae12010-06-05 23:22:07 +02004027 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004028 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004029 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004030 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4031 continue;
4032
4033 (void)syn_list_header(did_header, 999, id);
4034 did_header = TRUE;
4035 last_matchgroup = 0;
4036 if (spp->sp_type == SPTYPE_MATCH)
4037 {
4038 put_pattern("match", ' ', spp, attr);
4039 msg_putchar(' ');
4040 }
4041 else if (spp->sp_type == SPTYPE_START)
4042 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004043 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4044 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4045 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4046 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4047 while (idx < curwin->w_s->b_syn_patterns.ga_len
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004048 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004049 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004050 --idx;
4051 msg_putchar(' ');
4052 }
4053 syn_list_flags(namelist1, spp->sp_flags, attr);
4054
4055 if (spp->sp_cont_list != NULL)
4056 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4057
4058 if (spp->sp_syn.cont_in_list != NULL)
4059 put_id_list((char_u *)"containedin",
4060 spp->sp_syn.cont_in_list, attr);
4061
4062 if (spp->sp_next_list != NULL)
4063 {
4064 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4065 syn_list_flags(namelist2, spp->sp_flags, attr);
4066 }
4067 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4068 {
4069 if (spp->sp_flags & HL_SYNC_HERE)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004070 msg_puts_attr("grouphere", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004071 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004072 msg_puts_attr("groupthere", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004073 msg_putchar(' ');
4074 if (spp->sp_sync_idx >= 0)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004075 msg_outtrans(highlight_group_name(SYN_ITEMS(curwin->w_s)
4076 [spp->sp_sync_idx].sp_syn.id - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004077 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004078 msg_puts("NONE");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004079 msg_putchar(' ');
4080 }
4081 }
4082
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004083 // list the link, if there is one
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004084 if (highlight_link_id(id - 1) && (did_header || link_only) && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004085 {
4086 (void)syn_list_header(did_header, 999, id);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004087 msg_puts_attr("links to", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004088 msg_putchar(' ');
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004089 msg_outtrans(highlight_group_name(highlight_link_id(id - 1) - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004090 }
4091}
4092
4093 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004094syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004095{
4096 int i;
4097
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004098 for (i = 0; nlist[i].flag != 0; ++i)
4099 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004100 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004101 msg_puts_attr(nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004102 msg_putchar(' ');
4103 }
4104}
4105
4106/*
4107 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4108 */
4109 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004110syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004111{
4112 int endcol = 15;
4113
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004114 // slight hack: roughly duplicate the guts of syn_list_header()
Bram Moolenaar071d4272004-06-13 20:20:40 +00004115 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004116 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004117
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004118 if (msg_col >= endcol) // output at least one space
Bram Moolenaar071d4272004-06-13 20:20:40 +00004119 endcol = msg_col + 1;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004120 if (Columns <= endcol) // avoid hang for tiny window
Bram Moolenaar071d4272004-06-13 20:20:40 +00004121 endcol = Columns - 1;
4122
4123 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004124 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004125 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004126 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004127 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004128 }
4129 else
4130 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004131 msg_puts_attr("cluster", HL_ATTR(HLF_D));
4132 msg_puts("=NONE");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004133 }
4134}
4135
4136 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004137put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004138{
4139 short *p;
4140
Bram Moolenaar32526b32019-01-19 17:43:09 +01004141 msg_puts_attr((char *)name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004142 msg_putchar('=');
4143 for (p = list; *p; ++p)
4144 {
4145 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4146 {
4147 if (p[1])
Bram Moolenaar32526b32019-01-19 17:43:09 +01004148 msg_puts("ALLBUT");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004149 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004150 msg_puts("ALL");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004151 }
4152 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4153 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004154 msg_puts("TOP");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004155 }
4156 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4157 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004158 msg_puts("CONTAINED");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004159 }
4160 else if (*p >= SYNID_CLUSTER)
4161 {
4162 short scl_id = *p - SYNID_CLUSTER;
4163
4164 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004165 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004166 }
4167 else
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004168 msg_outtrans(highlight_group_name(*p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004169 if (p[1])
4170 msg_putchar(',');
4171 }
4172 msg_putchar(' ');
4173}
4174
4175 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004176put_pattern(
4177 char *s,
4178 int c,
4179 synpat_T *spp,
4180 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004181{
4182 long n;
4183 int mask;
4184 int first;
4185 static char *sepchars = "/+=-#@\"|'^&";
4186 int i;
4187
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004188 // May have to write "matchgroup=group"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004189 if (last_matchgroup != spp->sp_syn_match_id)
4190 {
4191 last_matchgroup = spp->sp_syn_match_id;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004192 msg_puts_attr("matchgroup", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004193 msg_putchar('=');
4194 if (last_matchgroup == 0)
4195 msg_outtrans((char_u *)"NONE");
4196 else
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004197 msg_outtrans(highlight_group_name(last_matchgroup - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004198 msg_putchar(' ');
4199 }
4200
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004201 // output the name of the pattern and an '=' or ' '
Bram Moolenaar32526b32019-01-19 17:43:09 +01004202 msg_puts_attr(s, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004203 msg_putchar(c);
4204
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004205 // output the pattern, in between a char that is not in the pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00004206 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4207 if (sepchars[++i] == NUL)
4208 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004209 i = 0; // no good char found, just use the first one
Bram Moolenaar071d4272004-06-13 20:20:40 +00004210 break;
4211 }
4212 msg_putchar(sepchars[i]);
4213 msg_outtrans(spp->sp_pattern);
4214 msg_putchar(sepchars[i]);
4215
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004216 // output any pattern options
Bram Moolenaar071d4272004-06-13 20:20:40 +00004217 first = TRUE;
4218 for (i = 0; i < SPO_COUNT; ++i)
4219 {
4220 mask = (1 << i);
4221 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4222 {
4223 if (!first)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004224 msg_putchar(','); // separate with commas
Bram Moolenaar32526b32019-01-19 17:43:09 +01004225 msg_puts(spo_name_tab[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004226 n = spp->sp_offsets[i];
4227 if (i != SPO_LC_OFF)
4228 {
4229 if (spp->sp_off_flags & mask)
4230 msg_putchar('s');
4231 else
4232 msg_putchar('e');
4233 if (n > 0)
4234 msg_putchar('+');
4235 }
4236 if (n || i == SPO_LC_OFF)
4237 msg_outnum(n);
4238 first = FALSE;
4239 }
4240 }
4241 msg_putchar(' ');
4242}
4243
4244/*
4245 * List or clear the keywords for one syntax group.
4246 * Return TRUE if the header has been printed.
4247 */
4248 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004249syn_list_keywords(
4250 int id,
4251 hashtab_T *ht,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004252 int did_header, // header has already been printed
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004253 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004254{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004255 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004256 hashitem_T *hi;
4257 keyentry_T *kp;
4258 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004259 int prev_contained = 0;
4260 short *prev_next_list = NULL;
4261 short *prev_cont_in_list = NULL;
4262 int prev_skipnl = 0;
4263 int prev_skipwhite = 0;
4264 int prev_skipempty = 0;
4265
Bram Moolenaar071d4272004-06-13 20:20:40 +00004266 /*
4267 * Unfortunately, this list of keywords is not sorted on alphabet but on
4268 * hash value...
4269 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004270 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004271 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004272 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004273 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004274 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004275 --todo;
4276 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004277 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004278 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004279 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004280 if (prev_contained != (kp->flags & HL_CONTAINED)
4281 || prev_skipnl != (kp->flags & HL_SKIPNL)
4282 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4283 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4284 || prev_cont_in_list != kp->k_syn.cont_in_list
4285 || prev_next_list != kp->next_list)
4286 outlen = 9999;
4287 else
4288 outlen = (int)STRLEN(kp->keyword);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004289 // output "contained" and "nextgroup" on each line
Bram Moolenaardad6b692005-01-25 22:14:34 +00004290 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004291 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004292 prev_contained = 0;
4293 prev_next_list = NULL;
4294 prev_cont_in_list = NULL;
4295 prev_skipnl = 0;
4296 prev_skipwhite = 0;
4297 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004298 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004299 did_header = TRUE;
4300 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004301 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004302 msg_puts_attr("contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004303 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004304 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004305 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004306 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004307 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004308 put_id_list((char_u *)"containedin",
4309 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004310 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004311 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004312 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004313 if (kp->next_list != prev_next_list)
4314 {
4315 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4316 msg_putchar(' ');
4317 prev_next_list = kp->next_list;
4318 if (kp->flags & HL_SKIPNL)
4319 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004320 msg_puts_attr("skipnl", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004321 msg_putchar(' ');
4322 prev_skipnl = (kp->flags & HL_SKIPNL);
4323 }
4324 if (kp->flags & HL_SKIPWHITE)
4325 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004326 msg_puts_attr("skipwhite", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004327 msg_putchar(' ');
4328 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4329 }
4330 if (kp->flags & HL_SKIPEMPTY)
4331 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004332 msg_puts_attr("skipempty", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004333 msg_putchar(' ');
4334 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4335 }
4336 }
4337 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004338 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004339 }
4340 }
4341 }
4342
4343 return did_header;
4344}
4345
4346 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004347syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004348{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004349 hashitem_T *hi;
4350 keyentry_T *kp;
4351 keyentry_T *kp_prev;
4352 keyentry_T *kp_next;
4353 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004354
Bram Moolenaardad6b692005-01-25 22:14:34 +00004355 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004356 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004357 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004358 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004359 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004360 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004361 --todo;
4362 kp_prev = NULL;
4363 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004364 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004365 if (kp->k_syn.id == id)
4366 {
4367 kp_next = kp->ke_next;
4368 if (kp_prev == NULL)
4369 {
4370 if (kp_next == NULL)
4371 hash_remove(ht, hi);
4372 else
4373 hi->hi_key = KE2HIKEY(kp_next);
4374 }
4375 else
4376 kp_prev->ke_next = kp_next;
4377 vim_free(kp->next_list);
4378 vim_free(kp->k_syn.cont_in_list);
4379 vim_free(kp);
4380 kp = kp_next;
4381 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004382 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004383 {
4384 kp_prev = kp;
4385 kp = kp->ke_next;
4386 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004387 }
4388 }
4389 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004390 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004391}
4392
4393/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004394 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004395 */
4396 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004397clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004398{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004399 hashitem_T *hi;
4400 int todo;
4401 keyentry_T *kp;
4402 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004403
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004404 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004405 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004406 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004407 if (!HASHITEM_EMPTY(hi))
4408 {
4409 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004410 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004411 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004412 kp_next = kp->ke_next;
4413 vim_free(kp->next_list);
4414 vim_free(kp->k_syn.cont_in_list);
4415 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004416 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004417 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004418 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004419 hash_clear(ht);
4420 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004421}
4422
4423/*
4424 * Add a keyword to the list of keywords.
4425 */
4426 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004427add_keyword(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004428 char_u *name, // name of keyword
4429 int id, // group ID for this keyword
4430 int flags, // flags for this keyword
4431 short *cont_in_list, // containedin for this keyword
4432 short *next_list, // nextgroup for this keyword
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004433 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004434{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004435 keyentry_T *kp;
4436 hashtab_T *ht;
4437 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004438 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004439 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004440 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441
Bram Moolenaar860cae12010-06-05 23:22:07 +02004442 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004443 name_ic = str_foldcase(name, (int)STRLEN(name),
4444 name_folded, MAXKEYWLEN + 1);
4445 else
4446 name_ic = name;
Bram Moolenaar47ed5532019-08-08 20:49:14 +02004447 kp = alloc(offsetof(keyentry_T, keyword) + STRLEN(name_ic) + 1);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004448 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004449 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004450 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004451 kp->k_syn.id = id;
4452 kp->k_syn.inc_tag = current_syn_inc_tag;
4453 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004454 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004455 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004456 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004457 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004458 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004459
Bram Moolenaar860cae12010-06-05 23:22:07 +02004460 if (curwin->w_s->b_syn_ic)
4461 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004462 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004463 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004464
Bram Moolenaardad6b692005-01-25 22:14:34 +00004465 hash = hash_hash(kp->keyword);
4466 hi = hash_lookup(ht, kp->keyword, hash);
4467 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004468 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004469 // new keyword, add to hashtable
Bram Moolenaardad6b692005-01-25 22:14:34 +00004470 kp->ke_next = NULL;
4471 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004472 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004473 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004474 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004475 // keyword already exists, prepend to list
Bram Moolenaardad6b692005-01-25 22:14:34 +00004476 kp->ke_next = HI2KE(hi);
4477 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004478 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004479}
4480
4481/*
4482 * Get the start and end of the group name argument.
4483 * Return a pointer to the first argument.
4484 * Return NULL if the end of the command was found instead of further args.
4485 */
4486 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004487get_group_name(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004488 char_u *arg, // start of the argument
4489 char_u **name_end) // pointer to end of the name
Bram Moolenaar071d4272004-06-13 20:20:40 +00004490{
4491 char_u *rest;
4492
4493 *name_end = skiptowhite(arg);
4494 rest = skipwhite(*name_end);
4495
4496 /*
4497 * Check if there are enough arguments. The first argument may be a
4498 * pattern, where '|' is allowed, so only check for NUL.
4499 */
4500 if (ends_excmd(*arg) || *rest == NUL)
4501 return NULL;
4502 return rest;
4503}
4504
4505/*
4506 * Check for syntax command option arguments.
4507 * This can be called at any place in the list of arguments, and just picks
4508 * out the arguments that are known. Can be called several times in a row to
4509 * collect all options in between other arguments.
4510 * Return a pointer to the next argument (which isn't an option).
4511 * Return NULL for any error;
4512 */
4513 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004514get_syn_options(
Bram Moolenaar1966c242020-04-20 22:42:32 +02004515 char_u *start, // next argument to be checked
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004516 syn_opt_arg_T *opt, // various things
Bram Moolenaarde318c52017-01-17 16:27:10 +01004517 int *conceal_char UNUSED,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004518 int skip) // TRUE if skipping over command
Bram Moolenaar071d4272004-06-13 20:20:40 +00004519{
Bram Moolenaar1966c242020-04-20 22:42:32 +02004520 char_u *arg = start;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004521 char_u *gname_start, *gname;
4522 int syn_id;
4523 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004524 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004525 int i;
4526 int fidx;
4527 static struct flag
4528 {
4529 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004530 int argtype;
4531 int flags;
4532 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4533 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4534 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4535 {"eExXtTeEnNdD", 0, HL_EXTEND},
4536 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4537 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4538 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4539 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4540 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4541 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4542 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4543 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4544 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004545 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4546 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4547 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004548 {"cCoOnNtTaAiInNsS", 1, 0},
4549 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4550 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004551 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004552 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004553
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004554 if (arg == NULL) // already detected error
Bram Moolenaar071d4272004-06-13 20:20:40 +00004555 return NULL;
4556
Bram Moolenaar860cae12010-06-05 23:22:07 +02004557#ifdef FEAT_CONCEAL
4558 if (curwin->w_s->b_syn_conceal)
4559 opt->flags |= HL_CONCEAL;
4560#endif
4561
Bram Moolenaar071d4272004-06-13 20:20:40 +00004562 for (;;)
4563 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004564 /*
4565 * This is used very often when a large number of keywords is defined.
4566 * Need to skip quickly when no option name is found.
4567 * Also avoid tolower(), it's slow.
4568 */
4569 if (strchr(first_letters, *arg) == NULL)
4570 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004571
K.Takataeeec2542021-06-02 13:28:16 +02004572 for (fidx = ARRAY_LENGTH(flagtab); --fidx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004573 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004574 p = flagtab[fidx].name;
4575 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4576 if (arg[len] != p[i] && arg[len] != p[i + 1])
4577 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004578 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004579 || (flagtab[fidx].argtype > 0
4580 ? arg[len] == '='
Bram Moolenaar1966c242020-04-20 22:42:32 +02004581 : ends_excmd2(start, arg + len))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004582 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004583 if (opt->keyword
4584 && (flagtab[fidx].flags == HL_DISPLAY
4585 || flagtab[fidx].flags == HL_FOLD
4586 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004587 // treat "display", "fold" and "extend" as a keyword
Bram Moolenaar071d4272004-06-13 20:20:40 +00004588 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004589 break;
4590 }
4591 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004592 if (fidx < 0) // no match found
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004593 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004594
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004595 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004596 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004597 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004598 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004599 emsg(_(e_contains_argument_not_accepted_here));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004600 return NULL;
4601 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004602 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004603 return NULL;
4604 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004605 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004606 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004607 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004608 return NULL;
4609 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004610 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004611 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004612 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004613 return NULL;
4614 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004615 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4616 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004617 // cchar=?
Bram Moolenaar860cae12010-06-05 23:22:07 +02004618 if (has_mbyte)
4619 {
Bram Moolenaar264b74f2019-01-24 17:18:42 +01004620#ifdef FEAT_CONCEAL
Bram Moolenaar860cae12010-06-05 23:22:07 +02004621 *conceal_char = mb_ptr2char(arg + 6);
Bram Moolenaar264b74f2019-01-24 17:18:42 +01004622#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004623 arg += mb_ptr2len(arg + 6) - 1;
4624 }
4625 else
Bram Moolenaar56be9502010-06-06 14:20:26 +02004626 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004627#ifdef FEAT_CONCEAL
4628 *conceal_char = arg[6];
4629#else
4630 ;
4631#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004632 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004633#ifdef FEAT_CONCEAL
4634 if (!vim_isprintc_strict(*conceal_char))
4635 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00004636 emsg(_(e_invalid_cchar_value));
Bram Moolenaar4124e722011-01-22 00:58:20 +01004637 return NULL;
4638 }
4639#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004640 arg = skipwhite(arg + 7);
4641 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004642 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004643 {
4644 opt->flags |= flagtab[fidx].flags;
4645 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004646
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004647 if (flagtab[fidx].flags == HL_SYNC_HERE
4648 || flagtab[fidx].flags == HL_SYNC_THERE)
4649 {
4650 if (opt->sync_idx == NULL)
4651 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004652 emsg(_(e_groupthere_not_accepted_here));
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004653 return NULL;
4654 }
4655 gname_start = arg;
4656 arg = skiptowhite(arg);
4657 if (gname_start == arg)
4658 return NULL;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02004659 gname = vim_strnsave(gname_start, arg - gname_start);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004660 if (gname == NULL)
4661 return NULL;
4662 if (STRCMP(gname, "NONE") == 0)
4663 *opt->sync_idx = NONE_IDX;
4664 else
4665 {
4666 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004667 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4668 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
Bram Moolenaar71ccd032020-06-12 22:59:11 +02004669 && SYN_ITEMS(curwin->w_s)[i].sp_type
4670 == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004671 {
4672 *opt->sync_idx = i;
4673 break;
4674 }
4675 if (i < 0)
4676 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004677 semsg(_(e_didnt_find_region_item_for_str), gname);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004678 vim_free(gname);
4679 return NULL;
4680 }
4681 }
4682
4683 vim_free(gname);
4684 arg = skipwhite(arg);
4685 }
4686#ifdef FEAT_FOLDING
4687 else if (flagtab[fidx].flags == HL_FOLD
4688 && foldmethodIsSyntax(curwin))
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004689 // Need to update folds later.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004690 foldUpdateAll(curwin);
4691#endif
4692 }
4693 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004694
4695 return arg;
4696}
4697
4698/*
4699 * Adjustments to syntax item when declared in a ":syn include"'d file.
4700 * Set the contained flag, and if the item is not already contained, add it
4701 * to the specified top-level group, if any.
4702 */
4703 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004704syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004705{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004706 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004707 return;
4708 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004709 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004710 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004711 // We have to alloc this, because syn_combine_list() will free it.
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004712 short *grp_list = ALLOC_MULT(short, 2);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004713 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004714
4715 if (grp_list != NULL)
4716 {
4717 grp_list[0] = id;
4718 grp_list[1] = 0;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004719 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list,
4720 &grp_list, CLUSTER_ADD);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004721 }
4722 }
4723}
4724
4725/*
4726 * Handle ":syntax include [@{group-name}] filename" command.
4727 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004728 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004729syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004730{
4731 char_u *arg = eap->arg;
4732 int sgl_id = 1;
4733 char_u *group_name_end;
4734 char_u *rest;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004735 char *errormsg = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004736 int prev_toplvl_grp;
4737 int prev_syn_inc_tag;
4738 int source = FALSE;
4739
4740 eap->nextcmd = find_nextcmd(arg);
4741 if (eap->skip)
4742 return;
4743
4744 if (arg[0] == '@')
4745 {
4746 ++arg;
4747 rest = get_group_name(arg, &group_name_end);
4748 if (rest == NULL)
4749 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004750 emsg(_(e_filename_required));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004751 return;
4752 }
4753 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004754 if (sgl_id == 0)
4755 return;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004756 // separate_nextcmd() and expand_filename() depend on this
Bram Moolenaar071d4272004-06-13 20:20:40 +00004757 eap->arg = rest;
4758 }
4759
4760 /*
4761 * Everything that's left, up to the next command, should be the
4762 * filename to include.
4763 */
Bram Moolenaar8071cb22019-07-12 17:58:01 +02004764 eap->argt |= (EX_XFILE | EX_NOSPC);
Bram Moolenaarac485062022-03-23 19:45:01 +00004765 separate_nextcmd(eap, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004766 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4767 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004768 // For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4769 // file. Need to expand the file name first. In other cases
4770 // ":runtime!" is used.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004771 source = TRUE;
4772 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4773 {
4774 if (errormsg != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004775 emsg(errormsg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004776 return;
4777 }
4778 }
4779
4780 /*
4781 * Save and restore the existing top-level grouplist id and ":syn
4782 * include" tag around the actual inclusion.
4783 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004784 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4785 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00004786 emsg(_(e_too_many_syntax_includes));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004787 return;
4788 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004789 prev_syn_inc_tag = current_syn_inc_tag;
4790 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004791 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4792 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01004793 if (source ? do_source(eap->arg, FALSE, DOSO_NONE, NULL) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004794 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004795 semsg(_(e_cant_open_file_str), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004796 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004797 current_syn_inc_tag = prev_syn_inc_tag;
4798}
4799
4800/*
4801 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4802 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004803 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004804syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004805{
4806 char_u *arg = eap->arg;
4807 char_u *group_name_end;
4808 int syn_id;
4809 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004810 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004811 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004812 char_u *kw;
4813 syn_opt_arg_T syn_opt_arg;
4814 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004815 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004816
4817 rest = get_group_name(arg, &group_name_end);
4818
4819 if (rest != NULL)
4820 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004821 if (eap->skip)
4822 syn_id = -1;
4823 else
4824 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004825 if (syn_id != 0)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004826 // allocate a buffer, for removing backslashes in the keyword
Bram Moolenaar964b3742019-05-24 18:54:09 +02004827 keyword_copy = alloc(STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004828 if (keyword_copy != NULL)
4829 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004830 syn_opt_arg.flags = 0;
4831 syn_opt_arg.keyword = TRUE;
4832 syn_opt_arg.sync_idx = NULL;
4833 syn_opt_arg.has_cont_list = FALSE;
4834 syn_opt_arg.cont_in_list = NULL;
4835 syn_opt_arg.next_list = NULL;
4836
Bram Moolenaar071d4272004-06-13 20:20:40 +00004837 /*
4838 * The options given apply to ALL keywords, so all options must be
4839 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004840 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004841 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004842 cnt = 0;
4843 p = keyword_copy;
Bram Moolenaar1966c242020-04-20 22:42:32 +02004844 for ( ; rest != NULL && !ends_excmd2(eap->arg, rest);
4845 rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004846 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004847 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4848 eap->skip);
Bram Moolenaar1966c242020-04-20 22:42:32 +02004849 if (rest == NULL || ends_excmd2(eap->arg, rest))
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004850 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004851 // Copy the keyword, removing backslashes, and add a NUL.
Bram Moolenaar1c465442017-03-12 20:10:05 +01004852 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004853 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004854 if (*rest == '\\' && rest[1] != NUL)
4855 ++rest;
4856 *p++ = *rest++;
4857 }
4858 *p++ = NUL;
4859 ++cnt;
4860 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004861
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004862 if (!eap->skip)
4863 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004864 // Adjust flags for use of ":syn include".
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004865 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4866
4867 /*
4868 * 2: Add an entry for each keyword.
4869 */
4870 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4871 {
4872 for (p = vim_strchr(kw, '['); ; )
4873 {
4874 if (p != NULL)
4875 *p = NUL;
4876 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004877 syn_opt_arg.cont_in_list,
4878 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004879 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004880 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004881 if (p[1] == NUL)
4882 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00004883 semsg(_(e_error_missing_rsb_str), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004884 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004885 }
4886 if (p[1] == ']')
4887 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004888 if (p[2] != NUL)
4889 {
Bram Moolenaard82a47d2022-01-05 20:24:39 +00004890 semsg(_(e_trailing_char_after_rsb_str_str),
4891 kw, &p[2]);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004892 goto error;
4893 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004894 kw = p + 1; // skip over the "]"
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004895 break;
4896 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004897 if (has_mbyte)
4898 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004899 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004900
4901 mch_memmove(p, p + 1, l);
4902 p += l;
4903 }
4904 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004905 {
4906 p[0] = p[1];
4907 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004908 }
4909 }
4910 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004911 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004912error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004913 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004914 vim_free(syn_opt_arg.cont_in_list);
4915 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004916 }
4917 }
4918
4919 if (rest != NULL)
Bram Moolenaar63b91732021-08-05 20:40:03 +02004920 set_nextcmd(eap, rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004921 else
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00004922 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004923
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004924 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004925 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004926}
4927
4928/*
4929 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4930 *
4931 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4932 */
4933 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004934syn_cmd_match(
4935 exarg_T *eap,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004936 int syncing) // TRUE for ":syntax sync match .. "
Bram Moolenaar071d4272004-06-13 20:20:40 +00004937{
4938 char_u *arg = eap->arg;
4939 char_u *group_name_end;
4940 char_u *rest;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004941 synpat_T item; // the item found in the line
Bram Moolenaar071d4272004-06-13 20:20:40 +00004942 int syn_id;
4943 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004944 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004945 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004946 int conceal_char = NUL;
Bram Moolenaar1966c242020-04-20 22:42:32 +02004947 int orig_called_emsg = called_emsg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004948
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004949 // Isolate the group name, check for validity
Bram Moolenaar071d4272004-06-13 20:20:40 +00004950 rest = get_group_name(arg, &group_name_end);
4951
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004952 // Get options before the pattern
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004953 syn_opt_arg.flags = 0;
4954 syn_opt_arg.keyword = FALSE;
4955 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4956 syn_opt_arg.has_cont_list = TRUE;
4957 syn_opt_arg.cont_list = NULL;
4958 syn_opt_arg.cont_in_list = NULL;
4959 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01004960 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004961
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004962 // get the pattern.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004963 init_syn_patterns();
Bram Moolenaara80faa82020-04-12 19:37:17 +02004964 CLEAR_FIELD(item);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004965 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004966 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4967 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004968
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004969 // Get options after the pattern
Bram Moolenaarde318c52017-01-17 16:27:10 +01004970 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004971
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004972 if (rest != NULL) // all arguments are valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00004973 {
4974 /*
4975 * Check for trailing command and illegal trailing arguments.
4976 */
Bram Moolenaar63b91732021-08-05 20:40:03 +02004977 set_nextcmd(eap, rest);
Bram Moolenaar1966c242020-04-20 22:42:32 +02004978 if (!ends_excmd2(eap->cmd, rest) || eap->skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004979 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004980 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004981 && (syn_id = syn_check_group(arg,
4982 (int)(group_name_end - arg))) != 0)
4983 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004984 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004985 /*
4986 * Store the pattern in the syn_items list
4987 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004988 idx = curwin->w_s->b_syn_patterns.ga_len;
4989 SYN_ITEMS(curwin->w_s)[idx] = item;
4990 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4991 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4992 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4993 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4994 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4995 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4996 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4997 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004998 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004999#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005000 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005001#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005002 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005003 curwin->w_s->b_syn_containedin = TRUE;
5004 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5005 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005006
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005007 // remember that we found a match for syncing on
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005008 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005009 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005010#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005011 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005012 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005013#endif
5014
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005015 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005016 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
5017 return; // don't free the progs and patterns now
Bram Moolenaar071d4272004-06-13 20:20:40 +00005018 }
5019 }
5020
5021 /*
5022 * Something failed, free the allocated memory.
5023 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005024 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005025 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005026 vim_free(syn_opt_arg.cont_list);
5027 vim_free(syn_opt_arg.cont_in_list);
5028 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005029
Bram Moolenaar1966c242020-04-20 22:42:32 +02005030 if (rest == NULL && called_emsg == orig_called_emsg)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005031 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005032}
5033
5034/*
5035 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5036 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5037 */
5038 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005039syn_cmd_region(
5040 exarg_T *eap,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005041 int syncing) // TRUE for ":syntax sync region .."
Bram Moolenaar071d4272004-06-13 20:20:40 +00005042{
5043 char_u *arg = eap->arg;
5044 char_u *group_name_end;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005045 char_u *rest; // next arg, NULL on error
Bram Moolenaar071d4272004-06-13 20:20:40 +00005046 char_u *key_end;
5047 char_u *key = NULL;
5048 char_u *p;
5049 int item;
5050#define ITEM_START 0
5051#define ITEM_SKIP 1
5052#define ITEM_END 2
5053#define ITEM_MATCHGROUP 3
5054 struct pat_ptr
5055 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005056 synpat_T *pp_synp; // pointer to syn_pattern
5057 int pp_matchgroup_id; // matchgroup ID
5058 struct pat_ptr *pp_next; // pointer to next pat_ptr
Bram Moolenaar071d4272004-06-13 20:20:40 +00005059 } *(pat_ptrs[3]);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005060 // patterns found in the line
Bram Moolenaar071d4272004-06-13 20:20:40 +00005061 struct pat_ptr *ppp;
5062 struct pat_ptr *ppp_next;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005063 int pat_count = 0; // nr of syn_patterns found
Bram Moolenaar071d4272004-06-13 20:20:40 +00005064 int syn_id;
5065 int matchgroup_id = 0;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005066 int not_enough = FALSE; // not enough arguments
5067 int illegal = FALSE; // illegal arguments
Bram Moolenaar071d4272004-06-13 20:20:40 +00005068 int success = FALSE;
5069 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005070 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005071 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005072
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005073 // Isolate the group name, check for validity
Bram Moolenaar071d4272004-06-13 20:20:40 +00005074 rest = get_group_name(arg, &group_name_end);
5075
5076 pat_ptrs[0] = NULL;
5077 pat_ptrs[1] = NULL;
5078 pat_ptrs[2] = NULL;
5079
5080 init_syn_patterns();
5081
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005082 syn_opt_arg.flags = 0;
5083 syn_opt_arg.keyword = FALSE;
5084 syn_opt_arg.sync_idx = NULL;
5085 syn_opt_arg.has_cont_list = TRUE;
5086 syn_opt_arg.cont_list = NULL;
5087 syn_opt_arg.cont_in_list = NULL;
5088 syn_opt_arg.next_list = NULL;
5089
Bram Moolenaar071d4272004-06-13 20:20:40 +00005090 /*
5091 * get the options, patterns and matchgroup.
5092 */
Bram Moolenaar1966c242020-04-20 22:42:32 +02005093 while (rest != NULL && !ends_excmd2(eap->cmd, rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005094 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005095 // Check for option arguments
Bram Moolenaarde318c52017-01-17 16:27:10 +01005096 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar1966c242020-04-20 22:42:32 +02005097 if (rest == NULL || ends_excmd2(eap->cmd, rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005098 break;
5099
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005100 // must be a pattern or matchgroup then
Bram Moolenaar071d4272004-06-13 20:20:40 +00005101 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005102 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005103 ++key_end;
5104 vim_free(key);
Bram Moolenaardf44a272020-06-07 20:49:05 +02005105 key = vim_strnsave_up(rest, key_end - rest);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005106 if (key == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00005107 {
5108 rest = NULL;
5109 break;
5110 }
5111 if (STRCMP(key, "MATCHGROUP") == 0)
5112 item = ITEM_MATCHGROUP;
5113 else if (STRCMP(key, "START") == 0)
5114 item = ITEM_START;
5115 else if (STRCMP(key, "END") == 0)
5116 item = ITEM_END;
5117 else if (STRCMP(key, "SKIP") == 0)
5118 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005119 if (pat_ptrs[ITEM_SKIP] != NULL) // one skip pattern allowed
Bram Moolenaar071d4272004-06-13 20:20:40 +00005120 {
5121 illegal = TRUE;
5122 break;
5123 }
5124 item = ITEM_SKIP;
5125 }
5126 else
5127 break;
5128 rest = skipwhite(key_end);
5129 if (*rest != '=')
5130 {
5131 rest = NULL;
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005132 semsg(_(e_missing_equal_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005133 break;
5134 }
5135 rest = skipwhite(rest + 1);
5136 if (*rest == NUL)
5137 {
5138 not_enough = TRUE;
5139 break;
5140 }
5141
5142 if (item == ITEM_MATCHGROUP)
5143 {
5144 p = skiptowhite(rest);
5145 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5146 matchgroup_id = 0;
5147 else
5148 {
5149 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5150 if (matchgroup_id == 0)
5151 {
5152 illegal = TRUE;
5153 break;
5154 }
5155 }
5156 rest = skipwhite(p);
5157 }
5158 else
5159 {
5160 /*
5161 * Allocate room for a syn_pattern, and link it in the list of
5162 * syn_patterns for this item, at the start (because the list is
5163 * used from end to start).
5164 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005165 ppp = ALLOC_ONE(struct pat_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005166 if (ppp == NULL)
5167 {
5168 rest = NULL;
5169 break;
5170 }
5171 ppp->pp_next = pat_ptrs[item];
5172 pat_ptrs[item] = ppp;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005173 ppp->pp_synp = ALLOC_CLEAR_ONE(synpat_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005174 if (ppp->pp_synp == NULL)
5175 {
5176 rest = NULL;
5177 break;
5178 }
5179
5180 /*
5181 * Get the syntax pattern and the following offset(s).
5182 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005183 // Enable the appropriate \z specials.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005184 if (item == ITEM_START)
5185 reg_do_extmatch = REX_SET;
5186 else if (item == ITEM_SKIP || item == ITEM_END)
5187 reg_do_extmatch = REX_USE;
5188 rest = get_syn_pattern(rest, ppp->pp_synp);
5189 reg_do_extmatch = 0;
5190 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005191 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005192 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5193 ppp->pp_matchgroup_id = matchgroup_id;
5194 ++pat_count;
5195 }
5196 }
5197 vim_free(key);
5198 if (illegal || not_enough)
5199 rest = NULL;
5200
5201 /*
5202 * Must have a "start" and "end" pattern.
5203 */
5204 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5205 pat_ptrs[ITEM_END] == NULL))
5206 {
5207 not_enough = TRUE;
5208 rest = NULL;
5209 }
5210
5211 if (rest != NULL)
5212 {
5213 /*
5214 * Check for trailing garbage or command.
5215 * If OK, add the item.
5216 */
Bram Moolenaar63b91732021-08-05 20:40:03 +02005217 set_nextcmd(eap, rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005218 if (!ends_excmd(*rest) || eap->skip)
5219 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005220 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005221 && (syn_id = syn_check_group(arg,
5222 (int)(group_name_end - arg))) != 0)
5223 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005224 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005225 /*
5226 * Store the start/skip/end in the syn_items list
5227 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005228 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005229 for (item = ITEM_START; item <= ITEM_END; ++item)
5230 {
5231 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5232 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005233 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5234 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5235 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005236 (item == ITEM_START) ? SPTYPE_START :
5237 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005238 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5239 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005240 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5241 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005242 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005243 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005244#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005245 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005246#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005247 if (item == ITEM_START)
5248 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005249 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005250 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005251 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005252 syn_opt_arg.cont_in_list;
5253 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005254 curwin->w_s->b_syn_containedin = TRUE;
5255 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005256 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005257 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005258 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005259 ++idx;
5260#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005261 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005262 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005263#endif
5264 }
5265 }
5266
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005267 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005268 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
5269 success = TRUE; // don't free the progs and patterns now
Bram Moolenaar071d4272004-06-13 20:20:40 +00005270 }
5271 }
5272
5273 /*
5274 * Free the allocated memory.
5275 */
5276 for (item = ITEM_START; item <= ITEM_END; ++item)
5277 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5278 {
Bram Moolenaar4bbfb0f2019-08-31 15:28:02 +02005279 if (!success && ppp->pp_synp != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005280 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005281 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005282 vim_free(ppp->pp_synp->sp_pattern);
5283 }
5284 vim_free(ppp->pp_synp);
5285 ppp_next = ppp->pp_next;
5286 vim_free(ppp);
5287 }
5288
5289 if (!success)
5290 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005291 vim_free(syn_opt_arg.cont_list);
5292 vim_free(syn_opt_arg.cont_in_list);
5293 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005294 if (not_enough)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005295 semsg(_(e_not_enough_arguments_syntax_region_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005296 else if (illegal || rest == NULL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005297 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005298 }
5299}
5300
5301/*
5302 * A simple syntax group ID comparison function suitable for use in qsort()
5303 */
5304 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005305syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005306{
5307 const short *s1 = v1;
5308 const short *s2 = v2;
5309
5310 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5311}
5312
5313/*
5314 * Combines lists of syntax clusters.
5315 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5316 */
5317 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005318syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005319{
5320 int count1 = 0;
5321 int count2 = 0;
5322 short *g1;
5323 short *g2;
5324 short *clstr = NULL;
5325 int count;
5326 int round;
5327
5328 /*
5329 * Handle degenerate cases.
5330 */
5331 if (*clstr2 == NULL)
5332 return;
5333 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5334 {
5335 if (list_op == CLUSTER_REPLACE)
5336 vim_free(*clstr1);
5337 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5338 *clstr1 = *clstr2;
5339 else
5340 vim_free(*clstr2);
5341 return;
5342 }
5343
5344 for (g1 = *clstr1; *g1; g1++)
5345 ++count1;
5346 for (g2 = *clstr2; *g2; g2++)
5347 ++count2;
5348
5349 /*
5350 * For speed purposes, sort both lists.
5351 */
5352 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5353 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5354
5355 /*
5356 * We proceed in two passes; in round 1, we count the elements to place
5357 * in the new list, and in round 2, we allocate and populate the new
5358 * list. For speed, we use a mergesort-like method, adding the smaller
5359 * of the current elements in each list to the new list.
5360 */
5361 for (round = 1; round <= 2; round++)
5362 {
5363 g1 = *clstr1;
5364 g2 = *clstr2;
5365 count = 0;
5366
5367 /*
5368 * First, loop through the lists until one of them is empty.
5369 */
5370 while (*g1 && *g2)
5371 {
5372 /*
5373 * We always want to add from the first list.
5374 */
5375 if (*g1 < *g2)
5376 {
5377 if (round == 2)
5378 clstr[count] = *g1;
5379 count++;
5380 g1++;
5381 continue;
5382 }
5383 /*
5384 * We only want to add from the second list if we're adding the
5385 * lists.
5386 */
5387 if (list_op == CLUSTER_ADD)
5388 {
5389 if (round == 2)
5390 clstr[count] = *g2;
5391 count++;
5392 }
5393 if (*g1 == *g2)
5394 g1++;
5395 g2++;
5396 }
5397
5398 /*
5399 * Now add the leftovers from whichever list didn't get finished
5400 * first. As before, we only want to add from the second list if
5401 * we're adding the lists.
5402 */
5403 for (; *g1; g1++, count++)
5404 if (round == 2)
5405 clstr[count] = *g1;
5406 if (list_op == CLUSTER_ADD)
5407 for (; *g2; g2++, count++)
5408 if (round == 2)
5409 clstr[count] = *g2;
5410
5411 if (round == 1)
5412 {
5413 /*
5414 * If the group ended up empty, we don't need to allocate any
5415 * space for it.
5416 */
5417 if (count == 0)
5418 {
5419 clstr = NULL;
5420 break;
5421 }
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005422 clstr = ALLOC_MULT(short, count + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005423 if (clstr == NULL)
5424 break;
5425 clstr[count] = 0;
5426 }
5427 }
5428
5429 /*
5430 * Finally, put the new list in place.
5431 */
5432 vim_free(*clstr1);
5433 vim_free(*clstr2);
5434 *clstr1 = clstr;
5435}
5436
5437/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005438 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005439 * If it is not found, 0 is returned.
5440 */
5441 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005442syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005443{
5444 int i;
5445 char_u *name_u;
5446
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005447 // Avoid using stricmp() too much, it's slow on some systems
Bram Moolenaar071d4272004-06-13 20:20:40 +00005448 name_u = vim_strsave_up(name);
5449 if (name_u == NULL)
5450 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005451 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5452 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5453 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005454 break;
5455 vim_free(name_u);
5456 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5457}
5458
5459/*
5460 * Like syn_scl_name2id(), but take a pointer + length argument.
5461 */
5462 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005463syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005464{
5465 char_u *name;
5466 int id = 0;
5467
5468 name = vim_strnsave(linep, len);
5469 if (name != NULL)
5470 {
5471 id = syn_scl_name2id(name);
5472 vim_free(name);
5473 }
5474 return id;
5475}
5476
5477/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005478 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005479 * The argument is a pointer to the name and the length of the name.
5480 * If it doesn't exist yet, a new entry is created.
5481 * Return 0 for failure.
5482 */
5483 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005484syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005485{
5486 int id;
5487 char_u *name;
5488
5489 name = vim_strnsave(pp, len);
5490 if (name == NULL)
5491 return 0;
5492
5493 id = syn_scl_name2id(name);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005494 if (id == 0) // doesn't exist yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00005495 id = syn_add_cluster(name);
5496 else
5497 vim_free(name);
5498 return id;
5499}
5500
5501/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005502 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005503 * "name" must be an allocated string, it will be consumed.
5504 * Return 0 for failure.
5505 */
5506 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005507syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005508{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005509 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005510
5511 /*
5512 * First call for this growarray: init growing array.
5513 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005514 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005515 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005516 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5517 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005518 }
5519
Bram Moolenaar42431a72011-04-01 14:44:59 +02005520 len = curwin->w_s->b_syn_clusters.ga_len;
5521 if (len >= MAX_CLUSTER_ID)
5522 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00005523 emsg(_(e_too_many_syntax_clusters));
Bram Moolenaar42431a72011-04-01 14:44:59 +02005524 vim_free(name);
5525 return 0;
5526 }
5527
Bram Moolenaar071d4272004-06-13 20:20:40 +00005528 /*
5529 * Make room for at least one other cluster entry.
5530 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005531 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005532 {
5533 vim_free(name);
5534 return 0;
5535 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005536
Bram Moolenaara80faa82020-04-12 19:37:17 +02005537 CLEAR_POINTER(&(SYN_CLSTR(curwin->w_s)[len]));
Bram Moolenaar860cae12010-06-05 23:22:07 +02005538 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5539 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5540 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5541 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005542
Bram Moolenaar217ad922005-03-20 22:37:15 +00005543 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005544 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005545 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005546 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005547
Bram Moolenaar071d4272004-06-13 20:20:40 +00005548 return len + SYNID_CLUSTER;
5549}
5550
5551/*
5552 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5553 * [add={groupname},..] [remove={groupname},..]".
5554 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005555 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005556syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005557{
5558 char_u *arg = eap->arg;
5559 char_u *group_name_end;
5560 char_u *rest;
5561 int scl_id;
5562 short *clstr_list;
5563 int got_clstr = FALSE;
5564 int opt_len;
5565 int list_op;
5566
5567 eap->nextcmd = find_nextcmd(arg);
5568 if (eap->skip)
5569 return;
5570
5571 rest = get_group_name(arg, &group_name_end);
5572
5573 if (rest != NULL)
5574 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005575 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5576 if (scl_id == 0)
5577 return;
5578 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005579
5580 for (;;)
5581 {
5582 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005583 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005584 {
5585 opt_len = 3;
5586 list_op = CLUSTER_ADD;
5587 }
5588 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005589 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005590 {
5591 opt_len = 6;
5592 list_op = CLUSTER_SUBTRACT;
5593 }
5594 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005595 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005596 {
5597 opt_len = 8;
5598 list_op = CLUSTER_REPLACE;
5599 }
5600 else
5601 break;
5602
5603 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005604 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005605 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005606 semsg(_(e_invalid_argument_str), rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005607 break;
5608 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005609 if (scl_id >= 0)
5610 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005611 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005612 else
5613 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005614 got_clstr = TRUE;
5615 }
5616
5617 if (got_clstr)
5618 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005619 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005620 syn_stack_free_all(curwin->w_s); // Need to recompute all.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005621 }
5622 }
5623
5624 if (!got_clstr)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005625 emsg(_(e_no_cluster_specified));
Bram Moolenaar1966c242020-04-20 22:42:32 +02005626 if (rest == NULL || !ends_excmd2(eap->cmd, rest))
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005627 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005628}
5629
5630/*
5631 * On first call for current buffer: Init growing array.
5632 */
5633 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005634init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005635{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005636 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5637 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005638}
5639
5640/*
5641 * Get one pattern for a ":syntax match" or ":syntax region" command.
5642 * Stores the pattern and program in a synpat_T.
5643 * Returns a pointer to the next argument, or NULL in case of an error.
5644 */
5645 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005646get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005647{
5648 char_u *end;
5649 int *p;
5650 int idx;
5651 char_u *cpo_save;
5652
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005653 // need at least three chars
Bram Moolenaar38219782015-08-11 15:27:13 +02005654 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005655 return NULL;
5656
Bram Moolenaare8c4abb2020-04-02 21:13:25 +02005657 end = skip_regexp(arg + 1, *arg, TRUE);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005658 if (*end != *arg) // end delimiter not found
Bram Moolenaar071d4272004-06-13 20:20:40 +00005659 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005660 semsg(_(e_pattern_delimiter_not_found_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005661 return NULL;
5662 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005663 // store the pattern and compiled regexp program
Bram Moolenaar71ccd032020-06-12 22:59:11 +02005664 if ((ci->sp_pattern = vim_strnsave(arg + 1, end - arg - 1)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005665 return NULL;
5666
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005667 // Make 'cpoptions' empty, to avoid the 'l' flag
Bram Moolenaar071d4272004-06-13 20:20:40 +00005668 cpo_save = p_cpo;
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01005669 p_cpo = empty_option;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005670 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5671 p_cpo = cpo_save;
5672
5673 if (ci->sp_prog == NULL)
5674 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005675 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005676#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005677 syn_clear_time(&ci->sp_time);
5678#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005679
5680 /*
5681 * Check for a match, highlight or region offset.
5682 */
5683 ++end;
5684 do
5685 {
5686 for (idx = SPO_COUNT; --idx >= 0; )
5687 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5688 break;
5689 if (idx >= 0)
5690 {
5691 p = &(ci->sp_offsets[idx]);
5692 if (idx != SPO_LC_OFF)
5693 switch (end[3])
5694 {
5695 case 's': break;
5696 case 'b': break;
5697 case 'e': idx += SPO_COUNT; break;
5698 default: idx = -1; break;
5699 }
5700 if (idx >= 0)
5701 {
5702 ci->sp_off_flags |= (1 << idx);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005703 if (idx == SPO_LC_OFF) // lc=99
Bram Moolenaar071d4272004-06-13 20:20:40 +00005704 {
5705 end += 3;
5706 *p = getdigits(&end);
5707
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005708 // "lc=" offset automatically sets "ms=" offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00005709 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5710 {
5711 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5712 ci->sp_offsets[SPO_MS_OFF] = *p;
5713 }
5714 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005715 else // yy=x+99
Bram Moolenaar071d4272004-06-13 20:20:40 +00005716 {
5717 end += 4;
5718 if (*end == '+')
5719 {
5720 ++end;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005721 *p = getdigits(&end); // positive offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00005722 }
5723 else if (*end == '-')
5724 {
5725 ++end;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005726 *p = -getdigits(&end); // negative offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00005727 }
5728 }
5729 if (*end != ',')
5730 break;
5731 ++end;
5732 }
5733 }
5734 } while (idx >= 0);
5735
Bram Moolenaar1966c242020-04-20 22:42:32 +02005736 if (!ends_excmd2(arg, end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005737 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005738 semsg(_(e_garbage_after_pattern_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005739 return NULL;
5740 }
5741 return skipwhite(end);
5742}
5743
5744/*
5745 * Handle ":syntax sync .." command.
5746 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005747 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005748syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005749{
5750 char_u *arg_start = eap->arg;
5751 char_u *arg_end;
5752 char_u *key = NULL;
5753 char_u *next_arg;
5754 int illegal = FALSE;
5755 int finished = FALSE;
5756 long n;
5757 char_u *cpo_save;
5758
Bram Moolenaar1966c242020-04-20 22:42:32 +02005759 if (ends_excmd2(eap->cmd, arg_start))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005760 {
5761 syn_cmd_list(eap, TRUE);
5762 return;
5763 }
5764
Bram Moolenaar1966c242020-04-20 22:42:32 +02005765 while (!ends_excmd2(eap->cmd, arg_start))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005766 {
5767 arg_end = skiptowhite(arg_start);
5768 next_arg = skipwhite(arg_end);
5769 vim_free(key);
Bram Moolenaardf44a272020-06-07 20:49:05 +02005770 key = vim_strnsave_up(arg_start, arg_end - arg_start);
Bram Moolenaar58bb61c2020-07-10 20:30:12 +02005771 if (key == NULL)
5772 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005773 if (STRCMP(key, "CCOMMENT") == 0)
5774 {
5775 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005776 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar1966c242020-04-20 22:42:32 +02005777 if (!ends_excmd2(eap->cmd, next_arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005778 {
5779 arg_end = skiptowhite(next_arg);
5780 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005781 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005782 (int)(arg_end - next_arg));
5783 next_arg = skipwhite(arg_end);
5784 }
5785 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005786 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005787 }
5788 else if ( STRNCMP(key, "LINES", 5) == 0
5789 || STRNCMP(key, "MINLINES", 8) == 0
5790 || STRNCMP(key, "MAXLINES", 8) == 0
5791 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5792 {
5793 if (key[4] == 'S')
5794 arg_end = key + 6;
5795 else if (key[0] == 'L')
5796 arg_end = key + 11;
5797 else
5798 arg_end = key + 9;
5799 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5800 {
5801 illegal = TRUE;
5802 break;
5803 }
5804 n = getdigits(&arg_end);
5805 if (!eap->skip)
5806 {
5807 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005808 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005809 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005810 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005811 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005812 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005813 }
5814 }
5815 else if (STRCMP(key, "FROMSTART") == 0)
5816 {
5817 if (!eap->skip)
5818 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005819 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5820 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005821 }
5822 }
5823 else if (STRCMP(key, "LINECONT") == 0)
5824 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005825 if (*next_arg == NUL) // missing pattern
Bram Moolenaar2795e212016-01-05 22:04:49 +01005826 {
5827 illegal = TRUE;
5828 break;
5829 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005830 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005831 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005832 emsg(_(e_syntax_sync_line_continuations_pattern_specified_twice));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005833 finished = TRUE;
5834 break;
5835 }
Bram Moolenaare8c4abb2020-04-02 21:13:25 +02005836 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005837 if (*arg_end != *next_arg) // end delimiter not found
Bram Moolenaar071d4272004-06-13 20:20:40 +00005838 {
5839 illegal = TRUE;
5840 break;
5841 }
5842
5843 if (!eap->skip)
5844 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005845 // store the pattern and compiled regexp program
Bram Moolenaar71ccd032020-06-12 22:59:11 +02005846 if ((curwin->w_s->b_syn_linecont_pat =
5847 vim_strnsave(next_arg + 1,
5848 arg_end - next_arg - 1)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005849 {
5850 finished = TRUE;
5851 break;
5852 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005853 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005854
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005855 // Make 'cpoptions' empty, to avoid the 'l' flag
Bram Moolenaar071d4272004-06-13 20:20:40 +00005856 cpo_save = p_cpo;
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01005857 p_cpo = empty_option;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005858 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005859 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005860 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005861#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005862 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5863#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005864
Bram Moolenaar860cae12010-06-05 23:22:07 +02005865 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005866 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01005867 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005868 finished = TRUE;
5869 break;
5870 }
5871 }
5872 next_arg = skipwhite(arg_end + 1);
5873 }
5874 else
5875 {
5876 eap->arg = next_arg;
5877 if (STRCMP(key, "MATCH") == 0)
5878 syn_cmd_match(eap, TRUE);
5879 else if (STRCMP(key, "REGION") == 0)
5880 syn_cmd_region(eap, TRUE);
5881 else if (STRCMP(key, "CLEAR") == 0)
5882 syn_cmd_clear(eap, TRUE);
5883 else
5884 illegal = TRUE;
5885 finished = TRUE;
5886 break;
5887 }
5888 arg_start = next_arg;
5889 }
5890 vim_free(key);
5891 if (illegal)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005892 semsg(_(e_illegal_arguments_str), arg_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005893 else if (!finished)
5894 {
Bram Moolenaar63b91732021-08-05 20:40:03 +02005895 set_nextcmd(eap, arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005896 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005897 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005898 }
5899}
5900
5901/*
5902 * Convert a line of highlight group names into a list of group ID numbers.
5903 * "arg" should point to the "contains" or "nextgroup" keyword.
5904 * "arg" is advanced to after the last group name.
5905 * Careful: the argument is modified (NULs added).
5906 * returns FAIL for some error, OK for success.
5907 */
5908 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005909get_id_list(
5910 char_u **arg,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005911 int keylen, // length of keyword
5912 short **list, // where to store the resulting list, if not
5913 // NULL, the list is silently skipped!
Bram Moolenaarde318c52017-01-17 16:27:10 +01005914 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005915{
5916 char_u *p = NULL;
5917 char_u *end;
5918 int round;
5919 int count;
5920 int total_count = 0;
5921 short *retval = NULL;
5922 char_u *name;
5923 regmatch_T regmatch;
5924 int id;
5925 int i;
5926 int failed = FALSE;
5927
5928 /*
5929 * We parse the list twice:
5930 * round == 1: count the number of items, allocate the array.
5931 * round == 2: fill the array with the items.
5932 * In round 1 new groups may be added, causing the number of items to
5933 * grow when a regexp is used. In that case round 1 is done once again.
5934 */
5935 for (round = 1; round <= 2; ++round)
5936 {
5937 /*
5938 * skip "contains"
5939 */
5940 p = skipwhite(*arg + keylen);
5941 if (*p != '=')
5942 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005943 semsg(_(e_missing_equal_sign_str), *arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005944 break;
5945 }
5946 p = skipwhite(p + 1);
Bram Moolenaar1966c242020-04-20 22:42:32 +02005947 if (ends_excmd2(*arg, p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005948 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005949 semsg(_(e_empty_argument_str), *arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005950 break;
5951 }
5952
5953 /*
5954 * parse the arguments after "contains"
5955 */
5956 count = 0;
Bram Moolenaar1966c242020-04-20 22:42:32 +02005957 while (!ends_excmd2(*arg, p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005958 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01005959 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005960 ;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005961 name = alloc(end - p + 3); // leave room for "^$"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005962 if (name == NULL)
5963 {
5964 failed = TRUE;
5965 break;
5966 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005967 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005968 if ( STRCMP(name + 1, "ALLBUT") == 0
5969 || STRCMP(name + 1, "ALL") == 0
5970 || STRCMP(name + 1, "TOP") == 0
5971 || STRCMP(name + 1, "CONTAINED") == 0)
5972 {
5973 if (TOUPPER_ASC(**arg) != 'C')
5974 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005975 semsg(_(e_str_not_allowed_here), name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005976 failed = TRUE;
5977 vim_free(name);
5978 break;
5979 }
5980 if (count != 0)
5981 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005982 semsg(_(e_str_must_be_first_in_contains_list), name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005983 failed = TRUE;
5984 vim_free(name);
5985 break;
5986 }
5987 if (name[1] == 'A')
Bram Moolenaar2e240bd2021-04-14 11:15:08 +02005988 id = SYNID_ALLBUT + current_syn_inc_tag;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005989 else if (name[1] == 'T')
Bram Moolenaar2e240bd2021-04-14 11:15:08 +02005990 {
5991 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
5992 id = curwin->w_s->b_syn_topgrp;
5993 else
5994 id = SYNID_TOP + current_syn_inc_tag;
5995 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005996 else
Bram Moolenaar2e240bd2021-04-14 11:15:08 +02005997 id = SYNID_CONTAINED + current_syn_inc_tag;
5998
Bram Moolenaar071d4272004-06-13 20:20:40 +00005999 }
6000 else if (name[1] == '@')
6001 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01006002 if (skip)
6003 id = -1;
6004 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01006005 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006006 }
6007 else
6008 {
6009 /*
6010 * Handle full group name.
6011 */
6012 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6013 id = syn_check_group(name + 1, (int)(end - p));
6014 else
6015 {
6016 /*
6017 * Handle match of regexp with group names.
6018 */
6019 *name = '^';
6020 STRCAT(name, "$");
6021 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6022 if (regmatch.regprog == NULL)
6023 {
6024 failed = TRUE;
6025 vim_free(name);
6026 break;
6027 }
6028
6029 regmatch.rm_ic = TRUE;
6030 id = 0;
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02006031 for (i = highlight_num_groups(); --i >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006032 {
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02006033 if (vim_regexec(&regmatch, highlight_group_name(i),
Bram Moolenaar071d4272004-06-13 20:20:40 +00006034 (colnr_T)0))
6035 {
6036 if (round == 2)
6037 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006038 // Got more items than expected; can happen
6039 // when adding items that match:
6040 // "contains=a.*b,axb".
6041 // Go back to first round
Bram Moolenaar071d4272004-06-13 20:20:40 +00006042 if (count >= total_count)
6043 {
6044 vim_free(retval);
6045 round = 1;
6046 }
6047 else
6048 retval[count] = i + 1;
6049 }
6050 ++count;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006051 id = -1; // remember that we found one
Bram Moolenaar071d4272004-06-13 20:20:40 +00006052 }
6053 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006054 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006055 }
6056 }
6057 vim_free(name);
6058 if (id == 0)
6059 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00006060 semsg(_(e_unknown_group_name_str), p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006061 failed = TRUE;
6062 break;
6063 }
6064 if (id > 0)
6065 {
6066 if (round == 2)
6067 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006068 // Got more items than expected, go back to first round
Bram Moolenaar071d4272004-06-13 20:20:40 +00006069 if (count >= total_count)
6070 {
6071 vim_free(retval);
6072 round = 1;
6073 }
6074 else
6075 retval[count] = id;
6076 }
6077 ++count;
6078 }
6079 p = skipwhite(end);
6080 if (*p != ',')
6081 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006082 p = skipwhite(p + 1); // skip comma in between arguments
Bram Moolenaar071d4272004-06-13 20:20:40 +00006083 }
6084 if (failed)
6085 break;
6086 if (round == 1)
6087 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006088 retval = ALLOC_MULT(short, count + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006089 if (retval == NULL)
6090 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006091 retval[count] = 0; // zero means end of the list
Bram Moolenaar071d4272004-06-13 20:20:40 +00006092 total_count = count;
6093 }
6094 }
6095
6096 *arg = p;
6097 if (failed || retval == NULL)
6098 {
6099 vim_free(retval);
6100 return FAIL;
6101 }
6102
6103 if (*list == NULL)
6104 *list = retval;
6105 else
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006106 vim_free(retval); // list already found, don't overwrite it
Bram Moolenaar071d4272004-06-13 20:20:40 +00006107
6108 return OK;
6109}
6110
6111/*
6112 * Make a copy of an ID list.
6113 */
6114 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006115copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006116{
6117 int len;
6118 int count;
6119 short *retval;
6120
6121 if (list == NULL)
6122 return NULL;
6123
6124 for (count = 0; list[count]; ++count)
6125 ;
6126 len = (count + 1) * sizeof(short);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006127 retval = alloc(len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006128 if (retval != NULL)
6129 mch_memmove(retval, list, (size_t)len);
6130
6131 return retval;
6132}
6133
6134/*
6135 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6136 * "cur_si" can be NULL if not checking the "containedin" list.
6137 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6138 * the current item.
6139 * This function is called very often, keep it fast!!
6140 */
6141 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006142in_id_list(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006143 stateitem_T *cur_si, // current item or NULL
6144 short *list, // id list
6145 struct sp_syn *ssp, // group id and ":syn include" tag of group
6146 int contained) // group id is contained
Bram Moolenaar071d4272004-06-13 20:20:40 +00006147{
6148 int retval;
6149 short *scl_list;
6150 short item;
6151 short id = ssp->id;
6152 static int depth = 0;
6153 int r;
6154
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006155 // If ssp has a "containedin" list and "cur_si" is in it, return TRUE.
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006156 if (cur_si != NULL && ssp->cont_in_list != NULL
6157 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006158 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006159 // Ignore transparent items without a contains argument. Double check
6160 // that we don't go back past the first one.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006161 while ((cur_si->si_flags & HL_TRANS_CONT)
6162 && cur_si > (stateitem_T *)(current_state.ga_data))
6163 --cur_si;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006164 // cur_si->si_idx is -1 for keywords, these never contain anything.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006165 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006166 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6167 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006168 return TRUE;
6169 }
6170
6171 if (list == NULL)
6172 return FALSE;
6173
6174 /*
6175 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6176 * inside anything. Only allow not-contained groups.
6177 */
6178 if (list == ID_LIST_ALL)
6179 return !contained;
6180
6181 /*
6182 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6183 * contains list. We also require that "id" is at the same ":syn include"
6184 * level as the list.
6185 */
6186 item = *list;
6187 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6188 {
6189 if (item < SYNID_TOP)
6190 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006191 // ALL or ALLBUT: accept all groups in the same file
Bram Moolenaar071d4272004-06-13 20:20:40 +00006192 if (item - SYNID_ALLBUT != ssp->inc_tag)
6193 return FALSE;
6194 }
6195 else if (item < SYNID_CONTAINED)
6196 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006197 // TOP: accept all not-contained groups in the same file
Bram Moolenaar071d4272004-06-13 20:20:40 +00006198 if (item - SYNID_TOP != ssp->inc_tag || contained)
6199 return FALSE;
6200 }
6201 else
6202 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006203 // CONTAINED: accept all contained groups in the same file
Bram Moolenaar071d4272004-06-13 20:20:40 +00006204 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6205 return FALSE;
6206 }
6207 item = *++list;
6208 retval = FALSE;
6209 }
6210 else
6211 retval = TRUE;
6212
6213 /*
6214 * Return "retval" if id is in the contains list.
6215 */
6216 while (item != 0)
6217 {
6218 if (item == id)
6219 return retval;
6220 if (item >= SYNID_CLUSTER)
6221 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006222 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006223 // restrict recursiveness to 30 to avoid an endless loop for a
6224 // cluster that includes itself (indirectly)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006225 if (scl_list != NULL && depth < 30)
6226 {
6227 ++depth;
6228 r = in_id_list(NULL, scl_list, ssp, contained);
6229 --depth;
6230 if (r)
6231 return retval;
6232 }
6233 }
6234 item = *++list;
6235 }
6236 return !retval;
6237}
6238
6239struct subcommand
6240{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006241 char *name; // subcommand name
6242 void (*func)(exarg_T *, int); // function to call
Bram Moolenaar071d4272004-06-13 20:20:40 +00006243};
6244
6245static struct subcommand subcommands[] =
6246{
6247 {"case", syn_cmd_case},
6248 {"clear", syn_cmd_clear},
6249 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006250 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006251 {"enable", syn_cmd_enable},
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006252 {"foldlevel", syn_cmd_foldlevel},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006253 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006254 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006255 {"keyword", syn_cmd_keyword},
6256 {"list", syn_cmd_list},
6257 {"manual", syn_cmd_manual},
6258 {"match", syn_cmd_match},
6259 {"on", syn_cmd_on},
6260 {"off", syn_cmd_off},
6261 {"region", syn_cmd_region},
6262 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006263 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006264 {"sync", syn_cmd_sync},
6265 {"", syn_cmd_list},
6266 {NULL, NULL}
6267};
6268
6269/*
6270 * ":syntax".
6271 * This searches the subcommands[] table for the subcommand name, and calls a
6272 * syntax_subcommand() function to do the rest.
6273 */
6274 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006275ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006276{
6277 char_u *arg = eap->arg;
6278 char_u *subcmd_end;
6279 char_u *subcmd_name;
6280 int i;
6281
6282 syn_cmdlinep = eap->cmdlinep;
6283
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006284 // isolate subcommand name
Bram Moolenaar071d4272004-06-13 20:20:40 +00006285 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6286 ;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02006287 subcmd_name = vim_strnsave(arg, subcmd_end - arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006288 if (subcmd_name != NULL)
6289 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006290 if (eap->skip) // skip error messages for all subcommands
Bram Moolenaar071d4272004-06-13 20:20:40 +00006291 ++emsg_skip;
6292 for (i = 0; ; ++i)
6293 {
6294 if (subcommands[i].name == NULL)
6295 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00006296 semsg(_(e_invalid_syntax_subcommand_str), subcmd_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006297 break;
6298 }
6299 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6300 {
6301 eap->arg = skipwhite(subcmd_end);
6302 (subcommands[i].func)(eap, FALSE);
6303 break;
6304 }
6305 }
6306 vim_free(subcmd_name);
6307 if (eap->skip)
6308 --emsg_skip;
6309 }
6310}
6311
Bram Moolenaar860cae12010-06-05 23:22:07 +02006312 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006313ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006314{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006315 char_u *old_value;
6316 char_u *new_value;
6317
Bram Moolenaar860cae12010-06-05 23:22:07 +02006318 if (curwin->w_s == &curwin->w_buffer->b_s)
6319 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006320 curwin->w_s = ALLOC_ONE(synblock_T);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006321 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006322 hash_init(&curwin->w_s->b_keywtab);
6323 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006324#ifdef FEAT_SPELL
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006325 // TODO: keep the spell checking as it was.
6326 curwin->w_p_spell = FALSE; // No spell checking
Bram Moolenaard1f76af2020-09-13 22:37:34 +02006327 // make sure option values are "empty_option" instead of NULL
Bram Moolenaar860cae12010-06-05 23:22:07 +02006328 clear_string_option(&curwin->w_s->b_p_spc);
6329 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006330 clear_string_option(&curwin->w_s->b_p_spl);
Bram Moolenaard1f76af2020-09-13 22:37:34 +02006331 clear_string_option(&curwin->w_s->b_p_spo);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006332#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006333 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006334 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006335
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006336 // save value of b:current_syntax
Bram Moolenaar1950c352010-06-06 15:21:10 +02006337 old_value = get_var_value((char_u *)"b:current_syntax");
6338 if (old_value != NULL)
6339 old_value = vim_strsave(old_value);
6340
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006341 // Apply the "syntax" autocommand event, this finds and loads the syntax
6342 // file.
Bram Moolenaar860cae12010-06-05 23:22:07 +02006343 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006344
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006345 // move value of b:current_syntax to w:current_syntax
Bram Moolenaar1950c352010-06-06 15:21:10 +02006346 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006347 if (new_value != NULL)
6348 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006349
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006350 // restore value of b:current_syntax
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006351 if (old_value == NULL)
6352 do_unlet((char_u *)"b:current_syntax", TRUE);
6353 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006354 {
6355 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6356 vim_free(old_value);
6357 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006358}
6359
6360 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006361syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006362{
6363 return (win->w_s->b_syn_patterns.ga_len != 0
6364 || win->w_s->b_syn_clusters.ga_len != 0
6365 || win->w_s->b_keywtab.ht_used > 0
6366 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006367}
6368
Bram Moolenaar071d4272004-06-13 20:20:40 +00006369
6370static enum
6371{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006372 EXP_SUBCMD, // expand ":syn" sub-commands
6373 EXP_CASE, // expand ":syn case" arguments
6374 EXP_SPELL, // expand ":syn spell" arguments
6375 EXP_SYNC // expand ":syn sync" arguments
Bram Moolenaar071d4272004-06-13 20:20:40 +00006376} expand_what;
6377
Bram Moolenaar4f688582007-07-24 12:34:30 +00006378/*
6379 * Reset include_link, include_default, include_none to 0.
6380 * Called when we are done expanding.
6381 */
6382 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006383reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006384{
6385 include_link = include_default = include_none = 0;
6386}
6387
6388/*
6389 * Handle command line completion for :match and :echohl command: Add "None"
6390 * as highlight group.
6391 */
6392 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006393set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006394{
6395 xp->xp_context = EXPAND_HIGHLIGHT;
6396 xp->xp_pattern = arg;
6397 include_none = 1;
6398}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006399
6400/*
6401 * Handle command line completion for :syntax command.
6402 */
6403 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006404set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006405{
6406 char_u *p;
6407
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006408 // Default: expand subcommands
Bram Moolenaar071d4272004-06-13 20:20:40 +00006409 xp->xp_context = EXPAND_SYNTAX;
6410 expand_what = EXP_SUBCMD;
6411 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006412 include_link = 0;
6413 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006414
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006415 // (part of) subcommand already typed
Bram Moolenaar071d4272004-06-13 20:20:40 +00006416 if (*arg != NUL)
6417 {
6418 p = skiptowhite(arg);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006419 if (*p != NUL) // past first word
Bram Moolenaar071d4272004-06-13 20:20:40 +00006420 {
6421 xp->xp_pattern = skipwhite(p);
6422 if (*skiptowhite(xp->xp_pattern) != NUL)
6423 xp->xp_context = EXPAND_NOTHING;
6424 else if (STRNICMP(arg, "case", p - arg) == 0)
6425 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006426 else if (STRNICMP(arg, "spell", p - arg) == 0)
6427 expand_what = EXP_SPELL;
6428 else if (STRNICMP(arg, "sync", p - arg) == 0)
6429 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006430 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6431 || STRNICMP(arg, "region", p - arg) == 0
6432 || STRNICMP(arg, "match", p - arg) == 0
6433 || STRNICMP(arg, "list", p - arg) == 0)
6434 xp->xp_context = EXPAND_HIGHLIGHT;
6435 else
6436 xp->xp_context = EXPAND_NOTHING;
6437 }
6438 }
6439}
6440
Bram Moolenaar071d4272004-06-13 20:20:40 +00006441/*
6442 * Function given to ExpandGeneric() to obtain the list syntax names for
6443 * expansion.
6444 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006445 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006446get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006447{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006448 switch (expand_what)
6449 {
6450 case EXP_SUBCMD:
6451 return (char_u *)subcommands[idx].name;
6452 case EXP_CASE:
6453 {
6454 static char *case_args[] = {"match", "ignore", NULL};
6455 return (char_u *)case_args[idx];
6456 }
6457 case EXP_SPELL:
6458 {
6459 static char *spell_args[] =
6460 {"toplevel", "notoplevel", "default", NULL};
6461 return (char_u *)spell_args[idx];
6462 }
6463 case EXP_SYNC:
6464 {
6465 static char *sync_args[] =
6466 {"ccomment", "clear", "fromstart",
6467 "linebreaks=", "linecont", "lines=", "match",
6468 "maxlines=", "minlines=", "region", NULL};
6469 return (char_u *)sync_args[idx];
6470 }
6471 }
6472 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006473}
6474
Bram Moolenaar071d4272004-06-13 20:20:40 +00006475
Bram Moolenaar071d4272004-06-13 20:20:40 +00006476/*
6477 * Function called for expression evaluation: get syntax ID at file position.
6478 */
6479 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006480syn_get_id(
6481 win_T *wp,
6482 long lnum,
6483 colnr_T col,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006484 int trans, // remove transparency
6485 int *spellp, // return: can do spell checking
6486 int keep_state) // keep state of char at "col"
Bram Moolenaar071d4272004-06-13 20:20:40 +00006487{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006488 // When the position is not after the current position and in the same
zeertzjqca7e86c2022-04-16 16:49:24 +01006489 // line of the same window with the same buffer, need to restart parsing.
6490 if (wp != syn_win
6491 || wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006492 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006493 || col < current_col)
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006494 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006495 else if (wp->w_buffer == syn_buf
6496 && lnum == current_lnum
6497 && col > current_col)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006498 // next_match may not be correct when moving around, e.g. with the
6499 // "skip" expression in searchpair()
Bram Moolenaar6773a342016-01-19 20:52:44 +01006500 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006501
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006502 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006503
6504 return (trans ? current_trans_id : current_id);
6505}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006506
Bram Moolenaar860cae12010-06-05 23:22:07 +02006507#if defined(FEAT_CONCEAL) || defined(PROTO)
6508/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006509 * Get extra information about the syntax item. Must be called right after
6510 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006511 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006512 * Returns the current flags.
6513 */
6514 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006515get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006516{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006517 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006518 return current_flags;
6519}
6520
6521/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006522 * Return conceal substitution character
6523 */
6524 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006525syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006526{
6527 return current_sub_char;
6528}
6529#endif
6530
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006531#if defined(FEAT_EVAL) || defined(PROTO)
6532/*
6533 * Return the syntax ID at position "i" in the current stack.
6534 * The caller must have called syn_get_id() before to fill the stack.
6535 * Returns -1 when "i" is out of range.
6536 */
6537 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006538syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006539{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006540 if (i >= current_state.ga_len)
6541 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006542 // Need to invalidate the state, because we didn't properly finish it
6543 // for the last character, "keep_state" was TRUE.
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006544 invalidate_current_state();
6545 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006546 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006547 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006548 return CUR_STATE(i).si_id;
6549}
6550#endif
6551
Bram Moolenaar071d4272004-06-13 20:20:40 +00006552#if defined(FEAT_FOLDING) || defined(PROTO)
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006553 static int
6554syn_cur_foldlevel(void)
6555{
6556 int level = 0;
6557 int i;
6558
6559 for (i = 0; i < current_state.ga_len; ++i)
6560 if (CUR_STATE(i).si_flags & HL_FOLD)
6561 ++level;
6562 return level;
6563}
6564
Bram Moolenaar071d4272004-06-13 20:20:40 +00006565/*
6566 * Function called to get folding level for line "lnum" in window "wp".
6567 */
6568 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006569syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006570{
6571 int level = 0;
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006572 int low_level;
6573 int cur_level;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006574
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006575 // Return quickly when there are no fold items at all.
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006576 if (wp->w_s->b_syn_folditems != 0
6577 && !wp->w_s->b_syn_error
6578# ifdef SYN_TIME_LIMIT
6579 && !wp->w_s->b_syn_slow
6580# endif
6581 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006582 {
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006583 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006584
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006585 // Start with the fold level at the start of the line.
6586 level = syn_cur_foldlevel();
6587
6588 if (wp->w_s->b_syn_foldlevel == SYNFLD_MINIMUM)
6589 {
6590 // Find the lowest fold level that is followed by a higher one.
6591 cur_level = level;
6592 low_level = cur_level;
6593 while (!current_finished)
6594 {
6595 (void)syn_current_attr(FALSE, FALSE, NULL, FALSE);
6596 cur_level = syn_cur_foldlevel();
6597 if (cur_level < low_level)
6598 low_level = cur_level;
6599 else if (cur_level > low_level)
6600 level = low_level;
6601 ++current_col;
6602 }
6603 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006604 }
6605 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006606 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006607 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006608 if (level < 0)
6609 level = 0;
6610 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006611 return level;
6612}
6613#endif
6614
Bram Moolenaar01615492015-02-03 13:00:38 +01006615#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006616/*
6617 * ":syntime".
6618 */
6619 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006620ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006621{
6622 if (STRCMP(eap->arg, "on") == 0)
6623 syn_time_on = TRUE;
6624 else if (STRCMP(eap->arg, "off") == 0)
6625 syn_time_on = FALSE;
6626 else if (STRCMP(eap->arg, "clear") == 0)
6627 syntime_clear();
6628 else if (STRCMP(eap->arg, "report") == 0)
6629 syntime_report();
6630 else
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00006631 semsg(_(e_invalid_argument_str), eap->arg);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006632}
6633
6634 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006635syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006636{
6637 profile_zero(&st->total);
6638 profile_zero(&st->slowest);
6639 st->count = 0;
6640 st->match = 0;
6641}
6642
6643/*
6644 * Clear the syntax timing for the current buffer.
6645 */
6646 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006647syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006648{
6649 int idx;
6650 synpat_T *spp;
6651
6652 if (!syntax_present(curwin))
6653 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006654 msg(_(msg_no_items));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006655 return;
6656 }
6657 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6658 {
6659 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6660 syn_clear_time(&spp->sp_time);
6661 }
6662}
6663
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006664/*
6665 * Function given to ExpandGeneric() to obtain the possible arguments of the
6666 * ":syntime {on,off,clear,report}" command.
6667 */
6668 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006669get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006670{
6671 switch (idx)
6672 {
6673 case 0: return (char_u *)"on";
6674 case 1: return (char_u *)"off";
6675 case 2: return (char_u *)"clear";
6676 case 3: return (char_u *)"report";
6677 }
6678 return NULL;
6679}
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006680
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006681typedef struct
6682{
6683 proftime_T total;
6684 int count;
6685 int match;
6686 proftime_T slowest;
6687 proftime_T average;
6688 int id;
6689 char_u *pattern;
6690} time_entry_T;
6691
6692 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006693syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006694{
6695 const time_entry_T *s1 = v1;
6696 const time_entry_T *s2 = v2;
6697
6698 return profile_cmp(&s1->total, &s2->total);
6699}
6700
6701/*
6702 * Clear the syntax timing for the current buffer.
6703 */
6704 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006705syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006706{
6707 int idx;
6708 synpat_T *spp;
Bram Moolenaar81319672020-11-25 11:47:39 +01006709# if defined(FEAT_RELTIME) && defined(FEAT_FLOAT)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006710 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006711# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006712 int len;
6713 proftime_T total_total;
6714 int total_count = 0;
6715 garray_T ga;
6716 time_entry_T *p;
6717
6718 if (!syntax_present(curwin))
6719 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006720 msg(_(msg_no_items));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006721 return;
6722 }
6723
6724 ga_init2(&ga, sizeof(time_entry_T), 50);
6725 profile_zero(&total_total);
6726 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6727 {
6728 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6729 if (spp->sp_time.count > 0)
6730 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006731 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006732 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6733 p->total = spp->sp_time.total;
6734 profile_add(&total_total, &spp->sp_time.total);
6735 p->count = spp->sp_time.count;
6736 p->match = spp->sp_time.match;
6737 total_count += spp->sp_time.count;
6738 p->slowest = spp->sp_time.slowest;
Bram Moolenaar81319672020-11-25 11:47:39 +01006739# if defined(FEAT_RELTIME) && defined(FEAT_FLOAT)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006740 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6741 p->average = tm;
6742# endif
6743 p->id = spp->sp_syn.id;
6744 p->pattern = spp->sp_pattern;
6745 ++ga.ga_len;
6746 }
6747 }
6748
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006749 // Sort on total time. Skip if there are no items to avoid passing NULL
6750 // pointer to qsort().
Bram Moolenaara2162552017-01-08 17:46:20 +01006751 if (ga.ga_len > 1)
6752 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006753 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006754
Bram Moolenaar32526b32019-01-19 17:43:09 +01006755 msg_puts_title(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6756 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006757 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6758 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006759 p = ((time_entry_T *)ga.ga_data) + idx;
6760
Bram Moolenaar32526b32019-01-19 17:43:09 +01006761 msg_puts(profile_msg(&p->total));
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006762 msg_puts(" "); // make sure there is always a separating space
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006763 msg_advance(13);
6764 msg_outnum(p->count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006765 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006766 msg_advance(20);
6767 msg_outnum(p->match);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006768 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006769 msg_advance(26);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006770 msg_puts(profile_msg(&p->slowest));
6771 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006772 msg_advance(38);
6773# ifdef FEAT_FLOAT
Bram Moolenaar32526b32019-01-19 17:43:09 +01006774 msg_puts(profile_msg(&p->average));
6775 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006776# endif
6777 msg_advance(50);
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02006778 msg_outtrans(highlight_group_name(p->id - 1));
Bram Moolenaar32526b32019-01-19 17:43:09 +01006779 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006780
6781 msg_advance(69);
6782 if (Columns < 80)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006783 len = 20; // will wrap anyway
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006784 else
6785 len = Columns - 70;
6786 if (len > (int)STRLEN(p->pattern))
6787 len = (int)STRLEN(p->pattern);
6788 msg_outtrans_len(p->pattern, len);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006789 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006790 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006791 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006792 if (!got_int)
6793 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006794 msg_puts("\n");
6795 msg_puts(profile_msg(&total_total));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006796 msg_advance(13);
6797 msg_outnum(total_count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006798 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006799 }
6800}
6801#endif
6802
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006803#endif // FEAT_SYN_HL