blob: 1fc205414e00f9c69ca87fe4f2ba4cee1e2b4042 [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 Moolenaar0d6f5d92019-12-05 21:33:15 +0100269static linenr_T current_lnum = 0; // lnum of current state
270static colnr_T current_col = 0; // column of current state
271static int current_state_stored = 0; // TRUE if stored current state
272 // after setting current_finished
273static int current_finished = 0; // current line has been finished
274static garray_T current_state // current stack of state_items
Bram Moolenaar071d4272004-06-13 20:20:40 +0000275 = {0, 0, 0, 0, NULL};
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100276static short *current_next_list = NULL; // when non-zero, nextgroup list
277static int current_next_flags = 0; // flags for current_next_list
278static int current_line_id = 0; // unique number for current line
Bram Moolenaar071d4272004-06-13 20:20:40 +0000279
280#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
281
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100282static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100283static int syn_match_linecont(linenr_T lnum);
284static void syn_start_line(void);
285static void syn_update_ends(int startofline);
286static void syn_stack_alloc(void);
287static int syn_stack_cleanup(void);
288static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
289static synstate_T *syn_stack_find_entry(linenr_T lnum);
290static synstate_T *store_current_state(void);
291static void load_current_state(synstate_T *from);
292static void invalidate_current_state(void);
293static int syn_stack_equal(synstate_T *sp);
294static void validate_current_state(void);
295static int syn_finish_line(int syncing);
296static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
297static int did_match_already(int idx, garray_T *gap);
298static stateitem_T *push_next_match(stateitem_T *cur_si);
299static void check_state_ends(void);
300static void update_si_attr(int idx);
301static void check_keepend(void);
302static void update_si_end(stateitem_T *sip, int startcol, int force);
303static short *copy_id_list(short *list);
304static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
305static int push_current_state(int idx);
306static void pop_current_state(void);
Bram Moolenaarf7512552013-06-06 14:55:19 +0200307#ifdef FEAT_PROFILE
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100308static void syn_clear_time(syn_time_T *tt);
309static void syntime_clear(void);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100310static void syntime_report(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200311static int syn_time_on = FALSE;
312# define IF_SYN_TIME(p) (p)
313#else
314# define IF_SYN_TIME(p) NULL
315typedef int syn_time_T;
316#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000317
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100318static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
319static 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 +0000320
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100321static void limit_pos(lpos_T *pos, lpos_T *limit);
322static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
323static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
324static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
325static char_u *syn_getcurline(void);
326static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
327static 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 +0100328static void syn_remove_pattern(synblock_T *block, int idx);
329static void syn_clear_pattern(synblock_T *block, int i);
330static void syn_clear_cluster(synblock_T *block, int i);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100331static void syn_clear_one(int id, int syncing);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100332static void syn_cmd_onoff(exarg_T *eap, char *name);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100333static void syn_lines_msg(void);
334static void syn_match_msg(void);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100335static void syn_list_one(int id, int syncing, int link_only);
336static void syn_list_cluster(int id);
337static void put_id_list(char_u *name, short *list, int attr);
338static void put_pattern(char *s, int c, synpat_T *spp, int attr);
339static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
340static void syn_clear_keyword(int id, hashtab_T *ht);
341static void clear_keywtab(hashtab_T *ht);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100342static int syn_scl_namen2id(char_u *linep, int len);
343static int syn_check_cluster(char_u *pp, int len);
344static int syn_add_cluster(char_u *name);
345static void init_syn_patterns(void);
346static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100347static int get_id_list(char_u **arg, int keylen, short **list, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100348static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000349
350/*
351 * Start the syntax recognition for a line. This function is normally called
352 * from the screen updating, once for each displayed line.
353 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
354 * it. Careful: curbuf and curwin are likely to point to another buffer and
355 * window.
356 */
357 void
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200358syntax_start(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000359{
360 synstate_T *p;
361 synstate_T *last_valid = NULL;
362 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000363 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000364 linenr_T parsed_lnum;
365 linenr_T first_stored;
366 int dist;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100367 static varnumber_T changedtick = 0; // remember the last change ID
Bram Moolenaar071d4272004-06-13 20:20:40 +0000368
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200369#ifdef FEAT_CONCEAL
370 current_sub_char = NUL;
371#endif
372
Bram Moolenaar071d4272004-06-13 20:20:40 +0000373 /*
374 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000375 * Also do this when a change was made, the current state may be invalid
376 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000377 */
Bram Moolenaarb681be12016-03-31 23:02:16 +0200378 if (syn_block != wp->w_s
379 || syn_buf != wp->w_buffer
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100380 || changedtick != CHANGEDTICK(syn_buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000381 {
382 invalidate_current_state();
383 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200384 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000385 }
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100386 changedtick = CHANGEDTICK(syn_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000387 syn_win = wp;
388
389 /*
390 * Allocate syntax stack when needed.
391 */
392 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200393 if (syn_block->b_sst_array == NULL)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100394 return; // out of memory
Bram Moolenaar860cae12010-06-05 23:22:07 +0200395 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000396
397 /*
398 * If the state of the end of the previous line is useful, store it.
399 */
400 if (VALID_STATE(&current_state)
401 && current_lnum < lnum
402 && current_lnum < syn_buf->b_ml.ml_line_count)
403 {
404 (void)syn_finish_line(FALSE);
405 if (!current_state_stored)
406 {
407 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000408 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000409 }
410
411 /*
412 * If the current_lnum is now the same as "lnum", keep the current
413 * state (this happens very often!). Otherwise invalidate
414 * current_state and figure it out below.
415 */
416 if (current_lnum != lnum)
417 invalidate_current_state();
418 }
419 else
420 invalidate_current_state();
421
422 /*
423 * Try to synchronize from a saved state in b_sst_array[].
424 * Only do this if lnum is not before and not to far beyond a saved state.
425 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200426 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000427 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100428 // Find last valid saved state before start_lnum.
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200429 FOR_ALL_SYNSTATES(syn_block, p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000430 {
431 if (p->sst_lnum > lnum)
432 break;
433 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
434 {
435 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200436 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000437 last_min_valid = p;
438 }
439 }
440 if (last_min_valid != NULL)
441 load_current_state(last_min_valid);
442 }
443
444 /*
445 * If "lnum" is before or far beyond a line with a saved state, need to
446 * re-synchronize.
447 */
448 if (INVALID_STATE(&current_state))
449 {
450 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200451 if (current_lnum == 1)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100452 // First line is always valid, no matter "minlines".
Bram Moolenaard6761c32011-06-19 04:54:21 +0200453 first_stored = 1;
454 else
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100455 // Need to parse "minlines" lines before state can be considered
456 // valid to store.
Bram Moolenaard6761c32011-06-19 04:54:21 +0200457 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000458 }
459 else
460 first_stored = current_lnum;
461
462 /*
463 * Advance from the sync point or saved state until the current line.
464 * Save some entries for syncing with later on.
465 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200466 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000467 dist = 999999;
468 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200469 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000470 while (current_lnum < lnum)
471 {
472 syn_start_line();
473 (void)syn_finish_line(FALSE);
474 ++current_lnum;
475
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100476 // If we parsed at least "minlines" lines or started at a valid
477 // state, the current state is considered valid.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000478 if (current_lnum >= first_stored)
479 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100480 // Check if the saved state entry is for the current line and is
481 // equal to the current state. If so, then validate all saved
482 // states that depended on a change before the parsed line.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000483 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000484 prev = syn_stack_find_entry(current_lnum - 1);
485 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200486 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000487 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000488 sp = prev;
489 while (sp != NULL && sp->sst_lnum < current_lnum)
490 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000491 if (sp != NULL
492 && sp->sst_lnum == current_lnum
493 && syn_stack_equal(sp))
494 {
495 parsed_lnum = current_lnum;
496 prev = sp;
497 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
498 {
499 if (sp->sst_lnum <= lnum)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100500 // valid state before desired line, use this one
Bram Moolenaar071d4272004-06-13 20:20:40 +0000501 prev = sp;
502 else if (sp->sst_change_lnum == 0)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100503 // past saved states depending on change, break here.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000504 break;
505 sp->sst_change_lnum = 0;
506 sp = sp->sst_next;
507 }
508 load_current_state(prev);
509 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100510 // Store the state at this line when it's the first one, the line
511 // where we start parsing, or some distance from the previously
512 // saved state. But only when parsed at least 'minlines'.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000513 else if (prev == NULL
514 || current_lnum == lnum
515 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000516 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000517 }
518
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100519 // This can take a long time: break when CTRL-C pressed. The current
520 // state will be wrong then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000521 line_breakcheck();
522 if (got_int)
523 {
524 current_lnum = lnum;
525 break;
526 }
527 }
528
529 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000530}
531
532/*
533 * We cannot simply discard growarrays full of state_items or buf_states; we
534 * have to manually release their extmatch pointers first.
535 */
536 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100537clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000538{
539 int i;
540 garray_T *gap;
541
542 if (p->sst_stacksize > SST_FIX_STATES)
543 {
544 gap = &(p->sst_union.sst_ga);
545 for (i = 0; i < gap->ga_len; i++)
546 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
547 ga_clear(gap);
548 }
549 else
550 {
551 for (i = 0; i < p->sst_stacksize; i++)
552 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
553 }
554}
555
556/*
557 * Cleanup the current_state stack.
558 */
559 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100560clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000561{
562 int i;
563 stateitem_T *sip;
564
565 sip = (stateitem_T *)(current_state.ga_data);
566 for (i = 0; i < current_state.ga_len; i++)
567 unref_extmatch(sip[i].si_extmatch);
568 ga_clear(&current_state);
569}
570
571/*
572 * Try to find a synchronisation point for line "lnum".
573 *
574 * This sets current_lnum and the current state. One of three methods is
575 * used:
576 * 1. Search backwards for the end of a C-comment.
577 * 2. Search backwards for given sync patterns.
578 * 3. Simply start on a given number of lines above "lnum".
579 */
580 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100581syn_sync(
582 win_T *wp,
583 linenr_T start_lnum,
584 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000585{
586 buf_T *curbuf_save;
587 win_T *curwin_save;
588 pos_T cursor_save;
589 int idx;
590 linenr_T lnum;
591 linenr_T end_lnum;
592 linenr_T break_lnum;
593 int had_sync_point;
594 stateitem_T *cur_si;
595 synpat_T *spp;
596 char_u *line;
597 int found_flags = 0;
598 int found_match_idx = 0;
599 linenr_T found_current_lnum = 0;
600 int found_current_col= 0;
601 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000602 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000603
604 /*
605 * Clear any current state that might be hanging around.
606 */
607 invalidate_current_state();
608
609 /*
610 * Start at least "minlines" back. Default starting point for parsing is
611 * there.
612 * Start further back, to avoid that scrolling backwards will result in
613 * resyncing for every line. Now it resyncs only one out of N lines,
614 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
615 * Watch out for overflow when minlines is MAXLNUM.
616 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200617 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000618 start_lnum = 1;
619 else
620 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200621 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000622 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200623 else if (syn_block->b_syn_sync_minlines < 10)
624 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000625 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200626 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
627 if (syn_block->b_syn_sync_maxlines != 0
628 && lnum > syn_block->b_syn_sync_maxlines)
629 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000630 if (lnum >= start_lnum)
631 start_lnum = 1;
632 else
633 start_lnum -= lnum;
634 }
635 current_lnum = start_lnum;
636
637 /*
638 * 1. Search backwards for the end of a C-style comment.
639 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200640 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000641 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100642 // Need to make syn_buf the current buffer for a moment, to be able to
643 // use find_start_comment().
Bram Moolenaar071d4272004-06-13 20:20:40 +0000644 curwin_save = curwin;
645 curwin = wp;
646 curbuf_save = curbuf;
647 curbuf = syn_buf;
648
649 /*
650 * Skip lines that end in a backslash.
651 */
652 for ( ; start_lnum > 1; --start_lnum)
653 {
654 line = ml_get(start_lnum - 1);
655 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
656 break;
657 }
658 current_lnum = start_lnum;
659
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100660 // set cursor to start of search
Bram Moolenaar071d4272004-06-13 20:20:40 +0000661 cursor_save = wp->w_cursor;
662 wp->w_cursor.lnum = start_lnum;
663 wp->w_cursor.col = 0;
664
665 /*
666 * If the line is inside a comment, need to find the syntax item that
667 * defines the comment.
668 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
669 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200670 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000671 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200672 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
673 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
674 == syn_block->b_syn_sync_id
675 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000676 {
677 validate_current_state();
678 if (push_current_state(idx) == OK)
679 update_si_attr(current_state.ga_len - 1);
680 break;
681 }
682 }
683
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100684 // restore cursor and buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +0000685 wp->w_cursor = cursor_save;
686 curwin = curwin_save;
687 curbuf = curbuf_save;
688 }
689
690 /*
691 * 2. Search backwards for given sync patterns.
692 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200693 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000694 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200695 if (syn_block->b_syn_sync_maxlines != 0
696 && start_lnum > syn_block->b_syn_sync_maxlines)
697 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000698 else
699 break_lnum = 0;
700
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000701 found_m_endpos.lnum = 0;
702 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000703 end_lnum = start_lnum;
704 lnum = start_lnum;
705 while (--lnum > break_lnum)
706 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100707 // This can take a long time: break when CTRL-C pressed.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000708 line_breakcheck();
709 if (got_int)
710 {
711 invalidate_current_state();
712 current_lnum = start_lnum;
713 break;
714 }
715
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100716 // Check if we have run into a valid saved state stack now.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000717 if (last_valid != NULL && lnum == last_valid->sst_lnum)
718 {
719 load_current_state(last_valid);
720 break;
721 }
722
723 /*
724 * Check if the previous line has the line-continuation pattern.
725 */
726 if (lnum > 1 && syn_match_linecont(lnum - 1))
727 continue;
728
729 /*
730 * Start with nothing on the state stack
731 */
732 validate_current_state();
733
734 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
735 {
736 syn_start_line();
737 for (;;)
738 {
739 had_sync_point = syn_finish_line(TRUE);
740 /*
741 * When a sync point has been found, remember where, and
742 * continue to look for another one, further on in the line.
743 */
744 if (had_sync_point && current_state.ga_len)
745 {
746 cur_si = &CUR_STATE(current_state.ga_len - 1);
747 if (cur_si->si_m_endpos.lnum > start_lnum)
748 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100749 // ignore match that goes to after where started
Bram Moolenaar071d4272004-06-13 20:20:40 +0000750 current_lnum = end_lnum;
751 break;
752 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000753 if (cur_si->si_idx < 0)
754 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100755 // Cannot happen?
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000756 found_flags = 0;
757 found_match_idx = KEYWORD_IDX;
758 }
759 else
760 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200761 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000762 found_flags = spp->sp_flags;
763 found_match_idx = spp->sp_sync_idx;
764 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000765 found_current_lnum = current_lnum;
766 found_current_col = current_col;
767 found_m_endpos = cur_si->si_m_endpos;
768 /*
769 * Continue after the match (be aware of a zero-length
770 * match).
771 */
772 if (found_m_endpos.lnum > current_lnum)
773 {
774 current_lnum = found_m_endpos.lnum;
775 current_col = found_m_endpos.col;
776 if (current_lnum >= end_lnum)
777 break;
778 }
779 else if (found_m_endpos.col > current_col)
780 current_col = found_m_endpos.col;
781 else
782 ++current_col;
783
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100784 // syn_current_attr() will have skipped the check for
785 // an item that ends here, need to do that now. Be
786 // careful not to go past the NUL.
Bram Moolenaar81366db2005-07-24 21:16:51 +0000787 prev_current_col = current_col;
788 if (syn_getcurline()[current_col] != NUL)
789 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000790 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000791 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000792 }
793 else
794 break;
795 }
796 }
797
798 /*
799 * If a sync point was encountered, break here.
800 */
801 if (found_flags)
802 {
803 /*
804 * Put the item that was specified by the sync point on the
805 * state stack. If there was no item specified, make the
806 * state stack empty.
807 */
808 clear_current_state();
809 if (found_match_idx >= 0
810 && push_current_state(found_match_idx) == OK)
811 update_si_attr(current_state.ga_len - 1);
812
813 /*
814 * When using "grouphere", continue from the sync point
815 * match, until the end of the line. Parsing starts at
816 * the next line.
817 * For "groupthere" the parsing starts at start_lnum.
818 */
819 if (found_flags & HL_SYNC_HERE)
820 {
821 if (current_state.ga_len)
822 {
823 cur_si = &CUR_STATE(current_state.ga_len - 1);
824 cur_si->si_h_startpos.lnum = found_current_lnum;
825 cur_si->si_h_startpos.col = found_current_col;
826 update_si_end(cur_si, (int)current_col, TRUE);
827 check_keepend();
828 }
829 current_col = found_m_endpos.col;
830 current_lnum = found_m_endpos.lnum;
831 (void)syn_finish_line(FALSE);
832 ++current_lnum;
833 }
834 else
835 current_lnum = start_lnum;
836
837 break;
838 }
839
840 end_lnum = lnum;
841 invalidate_current_state();
842 }
843
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100844 // Ran into start of the file or exceeded maximum number of lines
Bram Moolenaar071d4272004-06-13 20:20:40 +0000845 if (lnum <= break_lnum)
846 {
847 invalidate_current_state();
848 current_lnum = break_lnum + 1;
849 }
850 }
851
852 validate_current_state();
853}
854
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100855 static void
856save_chartab(char_u *chartab)
857{
858 if (syn_block->b_syn_isk != empty_option)
859 {
860 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
861 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
862 (size_t)32);
863 }
864}
865
866 static void
867restore_chartab(char_u *chartab)
868{
869 if (syn_win->w_s->b_syn_isk != empty_option)
870 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
871}
872
Bram Moolenaar071d4272004-06-13 20:20:40 +0000873/*
874 * Return TRUE if the line-continuation pattern matches in line "lnum".
875 */
876 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100877syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000878{
879 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100880 int r;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100881 char_u buf_chartab[32]; // chartab array for syn iskyeyword
Bram Moolenaar071d4272004-06-13 20:20:40 +0000882
Bram Moolenaar860cae12010-06-05 23:22:07 +0200883 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000884 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100885 // use syntax iskeyword option
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100886 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200887 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
888 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100889 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200890 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100891 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100892 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100893 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000894 }
895 return FALSE;
896}
897
898/*
899 * Prepare the current state for the start of a line.
900 */
901 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100902syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000903{
904 current_finished = FALSE;
905 current_col = 0;
906
907 /*
908 * Need to update the end of a start/skip/end that continues from the
909 * previous line and regions that have "keepend".
910 */
911 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +0200912 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000913 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +0200914 check_state_ends();
915 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000916
917 next_match_idx = -1;
918 ++current_line_id;
Bram Moolenaarea20de82017-06-24 22:52:24 +0200919#ifdef FEAT_CONCEAL
Bram Moolenaarcc0750d2017-06-24 22:29:24 +0200920 next_seqnr = 1;
Bram Moolenaarea20de82017-06-24 22:52:24 +0200921#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000922}
923
924/*
925 * Check for items in the stack that need their end updated.
926 * When "startofline" is TRUE the last item is always updated.
927 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
928 */
929 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100930syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000931{
932 stateitem_T *cur_si;
933 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000934 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000935
936 if (startofline)
937 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100938 // Check for a match carried over from a previous line with a
939 // contained region. The match ends as soon as the region ends.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000940 for (i = 0; i < current_state.ga_len; ++i)
941 {
942 cur_si = &CUR_STATE(i);
943 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +0200944 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +0000945 == SPTYPE_MATCH
946 && cur_si->si_m_endpos.lnum < current_lnum)
947 {
948 cur_si->si_flags |= HL_MATCHCONT;
949 cur_si->si_m_endpos.lnum = 0;
950 cur_si->si_m_endpos.col = 0;
951 cur_si->si_h_endpos = cur_si->si_m_endpos;
952 cur_si->si_ends = TRUE;
953 }
954 }
955 }
956
957 /*
958 * Need to update the end of a start/skip/end that continues from the
959 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000960 * influence contained items. If we've just removed "extend"
961 * (startofline == 0) then we should update ends of normal regions
962 * contained inside "keepend" because "extend" could have extended
963 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000964 * Then check for items ending in column 0.
965 */
966 i = current_state.ga_len - 1;
967 if (keepend_level >= 0)
968 for ( ; i > keepend_level; --i)
969 if (CUR_STATE(i).si_flags & HL_EXTEND)
970 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000971
972 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000973 for ( ; i < current_state.ga_len; ++i)
974 {
975 cur_si = &CUR_STATE(i);
976 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000977 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000978 || (i == current_state.ga_len - 1 && startofline))
979 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100980 cur_si->si_h_startpos.col = 0; // start highl. in col 0
Bram Moolenaar071d4272004-06-13 20:20:40 +0000981 cur_si->si_h_startpos.lnum = current_lnum;
982
983 if (!(cur_si->si_flags & HL_MATCHCONT))
984 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000985
986 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
987 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000988 }
989 }
990 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000991}
992
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100993/////////////////////////////////////////
994// Handling of the state stack cache.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000995
996/*
997 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
998 *
999 * To speed up syntax highlighting, the state stack for the start of some
1000 * lines is cached. These entries can be used to start parsing at that point.
1001 *
1002 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1003 * valid entries. b_sst_first points to the first one, then follow sst_next.
1004 * The entries are sorted on line number. The first entry is often for line 2
1005 * (line 1 always starts with an empty stack).
1006 * There is also a list for free entries. This construction is used to avoid
1007 * having to allocate and free memory blocks too often.
1008 *
1009 * When making changes to the buffer, this is logged in b_mod_*. When calling
1010 * update_screen() to update the display, it will call
1011 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1012 * entries. The entries which are inside the changed area are removed,
1013 * because they must be recomputed. Entries below the changed have their line
1014 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1015 * set to indicate that a check must be made if the changed lines would change
1016 * the cached entry.
1017 *
1018 * When later displaying lines, an entry is stored for each line. Displayed
1019 * lines are likely to be displayed again, in which case the state at the
1020 * start of the line is needed.
1021 * For not displayed lines, an entry is stored for every so many lines. These
1022 * entries will be used e.g., when scrolling backwards. The distance between
1023 * entries depends on the number of lines in the buffer. For small buffers
1024 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1025 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1026 */
1027
Bram Moolenaar860cae12010-06-05 23:22:07 +02001028 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001029syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001030{
1031 synstate_T *p;
1032
1033 if (block->b_sst_array != NULL)
1034 {
Bram Moolenaar00d253e2020-04-06 22:13:01 +02001035 FOR_ALL_SYNSTATES(block, p)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001036 clear_syn_state(p);
Bram Moolenaard23a8232018-02-10 18:45:26 +01001037 VIM_CLEAR(block->b_sst_array);
Bram Moolenaar95892c22018-09-28 22:26:54 +02001038 block->b_sst_first = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001039 block->b_sst_len = 0;
1040 }
1041}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001042/*
1043 * Free b_sst_array[] for buffer "buf".
1044 * Used when syntax items changed to force resyncing everywhere.
1045 */
1046 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001047syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001048{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001049#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001050 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001051#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001052
Bram Moolenaar860cae12010-06-05 23:22:07 +02001053 syn_stack_free_block(block);
1054
Bram Moolenaar071d4272004-06-13 20:20:40 +00001055#ifdef FEAT_FOLDING
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001056 // When using "syntax" fold method, must update all folds.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001057 FOR_ALL_WINDOWS(wp)
1058 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001059 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001060 foldUpdateAll(wp);
1061 }
1062#endif
1063}
1064
1065/*
1066 * Allocate the syntax state stack for syn_buf when needed.
1067 * If the number of entries in b_sst_array[] is much too big or a bit too
1068 * small, reallocate it.
1069 * Also used to allocate b_sst_array[] for the first time.
1070 */
1071 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001072syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001073{
1074 long len;
1075 synstate_T *to, *from;
1076 synstate_T *sstp;
1077
1078 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1079 if (len < SST_MIN_ENTRIES)
1080 len = SST_MIN_ENTRIES;
1081 else if (len > SST_MAX_ENTRIES)
1082 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001083 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001084 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001085 // Allocate 50% too much, to avoid reallocating too often.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001086 len = syn_buf->b_ml.ml_line_count;
1087 len = (len + len / 2) / SST_DIST + Rows * 2;
1088 if (len < SST_MIN_ENTRIES)
1089 len = SST_MIN_ENTRIES;
1090 else if (len > SST_MAX_ENTRIES)
1091 len = SST_MAX_ENTRIES;
1092
Bram Moolenaar860cae12010-06-05 23:22:07 +02001093 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001094 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001095 // When shrinking the array, cleanup the existing stack.
1096 // Make sure that all valid entries fit in the new array.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001097 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001098 && syn_stack_cleanup())
1099 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001100 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1101 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001102 }
1103
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001104 sstp = ALLOC_CLEAR_MULT(synstate_T, len);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001105 if (sstp == NULL) // out of memory!
Bram Moolenaar071d4272004-06-13 20:20:40 +00001106 return;
1107
1108 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001109 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001110 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001111 // Move the states from the old array to the new one.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001112 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001113 from = from->sst_next)
1114 {
1115 ++to;
1116 *to = *from;
1117 to->sst_next = to + 1;
1118 }
1119 }
1120 if (to != sstp - 1)
1121 {
1122 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001123 syn_block->b_sst_first = sstp;
1124 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001125 }
1126 else
1127 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001128 syn_block->b_sst_first = NULL;
1129 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001130 }
1131
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001132 // Create the list of free entries.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001133 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001134 while (++to < sstp + len)
1135 to->sst_next = to + 1;
1136 (sstp + len - 1)->sst_next = NULL;
1137
Bram Moolenaar860cae12010-06-05 23:22:07 +02001138 vim_free(syn_block->b_sst_array);
1139 syn_block->b_sst_array = sstp;
1140 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001141 }
1142}
1143
1144/*
1145 * Check for changes in a buffer to affect stored syntax states. Uses the
1146 * b_mod_* fields.
1147 * Called from update_screen(), before screen is being updated, once for each
1148 * displayed buffer.
1149 */
1150 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001151syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001152{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001153 win_T *wp;
1154
1155 syn_stack_apply_changes_block(&buf->b_s, buf);
1156
1157 FOR_ALL_WINDOWS(wp)
1158 {
1159 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1160 syn_stack_apply_changes_block(wp->w_s, buf);
1161 }
1162}
1163
1164 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001165syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001166{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001167 synstate_T *p, *prev, *np;
1168 linenr_T n;
1169
Bram Moolenaar071d4272004-06-13 20:20:40 +00001170 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001171 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001172 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001173 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001174 {
1175 n = p->sst_lnum + buf->b_mod_xlines;
1176 if (n <= buf->b_mod_bot)
1177 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001178 // this state is inside the changed area, remove it
Bram Moolenaar071d4272004-06-13 20:20:40 +00001179 np = p->sst_next;
1180 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001181 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001182 else
1183 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001184 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001185 p = np;
1186 continue;
1187 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001188 // This state is below the changed area. Remember the line
1189 // that needs to be parsed before this entry can be made valid
1190 // again.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001191 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1192 {
1193 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1194 p->sst_change_lnum += buf->b_mod_xlines;
1195 else
1196 p->sst_change_lnum = buf->b_mod_top;
1197 }
1198 if (p->sst_change_lnum == 0
1199 || p->sst_change_lnum < buf->b_mod_bot)
1200 p->sst_change_lnum = buf->b_mod_bot;
1201
1202 p->sst_lnum = n;
1203 }
1204 prev = p;
1205 p = p->sst_next;
1206 }
1207}
1208
1209/*
1210 * Reduce the number of entries in the state stack for syn_buf.
1211 * Returns TRUE if at least one entry was freed.
1212 */
1213 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001214syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001215{
1216 synstate_T *p, *prev;
1217 disptick_T tick;
1218 int above;
1219 int dist;
1220 int retval = FALSE;
1221
Bram Moolenaar95892c22018-09-28 22:26:54 +02001222 if (syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001223 return retval;
1224
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001225 // Compute normal distance between non-displayed entries.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001226 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001227 dist = 999999;
1228 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001229 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001230
1231 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001232 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001233 * be removed. Set "above" when the "tick" for the oldest entry is above
1234 * "b_sst_lasttick" (the display tick wraps around).
1235 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001236 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001237 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001238 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001239 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1240 {
1241 if (prev->sst_lnum + dist > p->sst_lnum)
1242 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001243 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244 {
1245 if (!above || p->sst_tick < tick)
1246 tick = p->sst_tick;
1247 above = TRUE;
1248 }
1249 else if (!above && p->sst_tick < tick)
1250 tick = p->sst_tick;
1251 }
1252 }
1253
1254 /*
1255 * Go through the list to make the entries for the oldest tick at an
1256 * interval of several lines.
1257 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001258 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001259 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1260 {
1261 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1262 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001263 // Move this entry from used list to free list
Bram Moolenaar071d4272004-06-13 20:20:40 +00001264 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001265 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001266 p = prev;
1267 retval = TRUE;
1268 }
1269 }
1270 return retval;
1271}
1272
1273/*
1274 * Free the allocated memory for a syn_state item.
1275 * Move the entry into the free list.
1276 */
1277 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001278syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001279{
1280 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001281 p->sst_next = block->b_sst_firstfree;
1282 block->b_sst_firstfree = p;
1283 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001284}
1285
1286/*
1287 * Find an entry in the list of state stacks at or before "lnum".
1288 * Returns NULL when there is no entry or the first entry is after "lnum".
1289 */
1290 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001291syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001292{
1293 synstate_T *p, *prev;
1294
1295 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001296 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001297 {
1298 if (p->sst_lnum == lnum)
1299 return p;
1300 if (p->sst_lnum > lnum)
1301 break;
1302 }
1303 return prev;
1304}
1305
1306/*
1307 * Try saving the current state in b_sst_array[].
1308 * The current state must be valid for the start of the current_lnum line!
1309 */
1310 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001311store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001312{
1313 int i;
1314 synstate_T *p;
1315 bufstate_T *bp;
1316 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001317 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001318
1319 /*
1320 * If the current state contains a start or end pattern that continues
1321 * from the previous line, we can't use it. Don't store it then.
1322 */
1323 for (i = current_state.ga_len - 1; i >= 0; --i)
1324 {
1325 cur_si = &CUR_STATE(i);
1326 if (cur_si->si_h_startpos.lnum >= current_lnum
1327 || cur_si->si_m_endpos.lnum >= current_lnum
1328 || cur_si->si_h_endpos.lnum >= current_lnum
1329 || (cur_si->si_end_idx
1330 && cur_si->si_eoe_pos.lnum >= current_lnum))
1331 break;
1332 }
1333 if (i >= 0)
1334 {
1335 if (sp != NULL)
1336 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001337 // find "sp" in the list and remove it
Bram Moolenaar860cae12010-06-05 23:22:07 +02001338 if (syn_block->b_sst_first == sp)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001339 // it's the first entry
Bram Moolenaar860cae12010-06-05 23:22:07 +02001340 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001341 else
1342 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001343 // find the entry just before this one to adjust sst_next
Bram Moolenaar00d253e2020-04-06 22:13:01 +02001344 FOR_ALL_SYNSTATES(syn_block, p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001345 if (p->sst_next == sp)
1346 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001347 if (p != NULL) // just in case
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001348 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001349 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001350 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001351 sp = NULL;
1352 }
1353 }
1354 else if (sp == NULL || sp->sst_lnum != current_lnum)
1355 {
1356 /*
1357 * Add a new entry
1358 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001359 // If no free items, cleanup the array first.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001360 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001361 {
1362 (void)syn_stack_cleanup();
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001363 // "sp" may have been moved to the freelist now
Bram Moolenaar071d4272004-06-13 20:20:40 +00001364 sp = syn_stack_find_entry(current_lnum);
1365 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001366 // Still no free items? Must be a strange problem...
Bram Moolenaar860cae12010-06-05 23:22:07 +02001367 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001368 sp = NULL;
1369 else
1370 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001371 // Take the first item from the free list and put it in the used
1372 // list, after *sp
Bram Moolenaar860cae12010-06-05 23:22:07 +02001373 p = syn_block->b_sst_firstfree;
1374 syn_block->b_sst_firstfree = p->sst_next;
1375 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001376 if (sp == NULL)
1377 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001378 // Insert in front of the list
Bram Moolenaar860cae12010-06-05 23:22:07 +02001379 p->sst_next = syn_block->b_sst_first;
1380 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001381 }
1382 else
1383 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001384 // insert in list after *sp
Bram Moolenaar071d4272004-06-13 20:20:40 +00001385 p->sst_next = sp->sst_next;
1386 sp->sst_next = p;
1387 }
1388 sp = p;
1389 sp->sst_stacksize = 0;
1390 sp->sst_lnum = current_lnum;
1391 }
1392 }
1393 if (sp != NULL)
1394 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001395 // When overwriting an existing state stack, clear it first
Bram Moolenaar071d4272004-06-13 20:20:40 +00001396 clear_syn_state(sp);
1397 sp->sst_stacksize = current_state.ga_len;
1398 if (current_state.ga_len > SST_FIX_STATES)
1399 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001400 // Need to clear it, might be something remaining from when the
1401 // length was less than SST_FIX_STATES.
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001402 ga_init2(&sp->sst_union.sst_ga, sizeof(bufstate_T), 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001403 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1404 sp->sst_stacksize = 0;
1405 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001406 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001407 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1408 }
1409 else
1410 bp = sp->sst_union.sst_stack;
1411 for (i = 0; i < sp->sst_stacksize; ++i)
1412 {
1413 bp[i].bs_idx = CUR_STATE(i).si_idx;
1414 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001415#ifdef FEAT_CONCEAL
1416 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1417 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1418#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001419 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1420 }
1421 sp->sst_next_flags = current_next_flags;
1422 sp->sst_next_list = current_next_list;
1423 sp->sst_tick = display_tick;
1424 sp->sst_change_lnum = 0;
1425 }
1426 current_state_stored = TRUE;
1427 return sp;
1428}
1429
1430/*
1431 * Copy a state stack from "from" in b_sst_array[] to current_state;
1432 */
1433 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001434load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001435{
1436 int i;
1437 bufstate_T *bp;
1438
1439 clear_current_state();
1440 validate_current_state();
1441 keepend_level = -1;
1442 if (from->sst_stacksize
1443 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1444 {
1445 if (from->sst_stacksize > SST_FIX_STATES)
1446 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1447 else
1448 bp = from->sst_union.sst_stack;
1449 for (i = 0; i < from->sst_stacksize; ++i)
1450 {
1451 CUR_STATE(i).si_idx = bp[i].bs_idx;
1452 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001453#ifdef FEAT_CONCEAL
1454 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1455 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1456#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001457 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1458 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1459 keepend_level = i;
1460 CUR_STATE(i).si_ends = FALSE;
1461 CUR_STATE(i).si_m_lnum = 0;
1462 if (CUR_STATE(i).si_idx >= 0)
1463 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001464 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001465 else
1466 CUR_STATE(i).si_next_list = NULL;
1467 update_si_attr(i);
1468 }
1469 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001470 }
1471 current_next_list = from->sst_next_list;
1472 current_next_flags = from->sst_next_flags;
1473 current_lnum = from->sst_lnum;
1474}
1475
1476/*
1477 * Compare saved state stack "*sp" with the current state.
1478 * Return TRUE when they are equal.
1479 */
1480 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001481syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001482{
1483 int i, j;
1484 bufstate_T *bp;
1485 reg_extmatch_T *six, *bsx;
1486
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001487 // First a quick check if the stacks have the same size end nextlist.
zeertzjq101d57b2022-07-31 18:34:32 +01001488 if (sp->sst_stacksize != current_state.ga_len
1489 || sp->sst_next_list != current_next_list)
1490 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001491
zeertzjq101d57b2022-07-31 18:34:32 +01001492 // Need to compare all states on both stacks.
1493 if (sp->sst_stacksize > SST_FIX_STATES)
1494 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1495 else
1496 bp = sp->sst_union.sst_stack;
1497
1498 for (i = current_state.ga_len; --i >= 0; )
1499 {
1500 // If the item has another index the state is different.
1501 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1502 break;
1503 if (bp[i].bs_extmatch == CUR_STATE(i).si_extmatch)
1504 continue;
1505 // When the extmatch pointers are different, the strings in them can
1506 // still be the same. Check if the extmatch references are equal.
1507 bsx = bp[i].bs_extmatch;
1508 six = CUR_STATE(i).si_extmatch;
1509 // If one of the extmatch pointers is NULL the states are different.
1510 if (bsx == NULL || six == NULL)
1511 break;
1512 for (j = 0; j < NSUBEXP; ++j)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001513 {
zeertzjq101d57b2022-07-31 18:34:32 +01001514 // Check each referenced match string. They must all be equal.
1515 if (bsx->matches[j] != six->matches[j])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001516 {
zeertzjq101d57b2022-07-31 18:34:32 +01001517 // If the pointer is different it can still be the same text.
1518 // Compare the strings, ignore case when the start item has the
1519 // sp_ic flag set.
1520 if (bsx->matches[j] == NULL || six->matches[j] == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001521 break;
zeertzjq101d57b2022-07-31 18:34:32 +01001522 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
1523 ? MB_STRICMP(bsx->matches[j], six->matches[j]) != 0
1524 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001525 break;
1526 }
1527 }
zeertzjq101d57b2022-07-31 18:34:32 +01001528 if (j != NSUBEXP)
1529 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001530 }
zeertzjq101d57b2022-07-31 18:34:32 +01001531 return i < 0 ? TRUE : FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001532}
1533
1534/*
1535 * We stop parsing syntax above line "lnum". If the stored state at or below
1536 * this line depended on a change before it, it now depends on the line below
1537 * the last parsed line.
1538 * The window looks like this:
1539 * line which changed
1540 * displayed line
1541 * displayed line
1542 * lnum -> line below window
1543 */
1544 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001545syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001546{
1547 synstate_T *sp;
1548
1549 sp = syn_stack_find_entry(lnum);
1550 if (sp != NULL && sp->sst_lnum < lnum)
1551 sp = sp->sst_next;
1552
1553 if (sp != NULL && sp->sst_change_lnum != 0)
1554 sp->sst_change_lnum = lnum;
1555}
1556
1557/*
1558 * End of handling of the state stack.
1559 ****************************************/
1560
1561 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001562invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001563{
1564 clear_current_state();
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001565 current_state.ga_itemsize = 0; // mark current_state invalid
Bram Moolenaar071d4272004-06-13 20:20:40 +00001566 current_next_list = NULL;
1567 keepend_level = -1;
1568}
1569
1570 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001571validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001572{
1573 current_state.ga_itemsize = sizeof(stateitem_T);
1574 current_state.ga_growsize = 3;
1575}
1576
1577/*
1578 * Return TRUE if the syntax at start of lnum changed since last time.
1579 * This will only be called just after get_syntax_attr() for the previous
1580 * line, to check if the next line needs to be redrawn too.
1581 */
1582 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001583syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001584{
1585 int retval = TRUE;
1586 synstate_T *sp;
1587
Bram Moolenaar071d4272004-06-13 20:20:40 +00001588 /*
1589 * Check the state stack when:
1590 * - lnum is just below the previously syntaxed line.
1591 * - lnum is not before the lines with saved states.
1592 * - lnum is not past the lines with saved states.
1593 * - lnum is at or before the last changed line.
1594 */
1595 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1596 {
1597 sp = syn_stack_find_entry(lnum);
1598 if (sp != NULL && sp->sst_lnum == lnum)
1599 {
1600 /*
1601 * finish the previous line (needed when not all of the line was
1602 * drawn)
1603 */
1604 (void)syn_finish_line(FALSE);
1605
1606 /*
1607 * Compare the current state with the previously saved state of
1608 * the line.
1609 */
1610 if (syn_stack_equal(sp))
1611 retval = FALSE;
1612
1613 /*
1614 * Store the current state in b_sst_array[] for later use.
1615 */
1616 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001617 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001618 }
1619 }
1620
Bram Moolenaar071d4272004-06-13 20:20:40 +00001621 return retval;
1622}
1623
1624/*
1625 * Finish the current line.
1626 * This doesn't return any attributes, it only gets the state at the end of
1627 * the line. It can start anywhere in the line, as long as the current state
1628 * is valid.
1629 */
1630 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001631syn_finish_line(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001632 int syncing) // called for syncing
Bram Moolenaar071d4272004-06-13 20:20:40 +00001633{
1634 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001635 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001636
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001637 while (!current_finished)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001638 {
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001639 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1640 /*
1641 * When syncing, and found some item, need to check the item.
1642 */
1643 if (syncing && current_state.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001644 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001645 /*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001646 * Check for match with sync item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001647 */
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001648 cur_si = &CUR_STATE(current_state.ga_len - 1);
1649 if (cur_si->si_idx >= 0
1650 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1651 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1652 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001653
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001654 // syn_current_attr() will have skipped the check for an item
1655 // that ends here, need to do that now. Be careful not to go
1656 // past the NUL.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001657 prev_current_col = current_col;
1658 if (syn_getcurline()[current_col] != NUL)
1659 ++current_col;
1660 check_state_ends();
1661 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001662 }
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001663 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001664 }
1665 return FALSE;
1666}
1667
1668/*
1669 * Return highlight attributes for next character.
1670 * Must first call syntax_start() once for the line.
1671 * "col" is normally 0 for the first use in a line, and increments by one each
1672 * time. It's allowed to skip characters and to stop before the end of the
1673 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001674 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1675 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001676 */
1677 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001678get_syntax_attr(
1679 colnr_T col,
1680 int *can_spell,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001681 int keep_state) // keep state of char at "col"
Bram Moolenaar071d4272004-06-13 20:20:40 +00001682{
1683 int attr = 0;
1684
Bram Moolenaar349955a2007-08-14 21:07:36 +00001685 if (can_spell != NULL)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001686 // Default: Only do spelling when there is no @Spell cluster or when
1687 // ":syn spell toplevel" was used.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001688 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1689 ? (syn_block->b_spell_cluster_id == 0)
1690 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001691
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001692 // check for out of memory situation
Bram Moolenaar860cae12010-06-05 23:22:07 +02001693 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001694 return 0;
1695
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001696 // After 'synmaxcol' the attribute is always zero.
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001697 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001698 {
1699 clear_current_state();
1700#ifdef FEAT_EVAL
1701 current_id = 0;
1702 current_trans_id = 0;
1703#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001704#ifdef FEAT_CONCEAL
1705 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001706 current_seqnr = 0;
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001707#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001708 return 0;
1709 }
1710
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001711 // Make sure current_state is valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00001712 if (INVALID_STATE(&current_state))
1713 validate_current_state();
1714
1715 /*
1716 * Skip from the current column to "col", get the attributes for "col".
1717 */
1718 while (current_col <= col)
1719 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001720 attr = syn_current_attr(FALSE, TRUE, can_spell,
1721 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001722 ++current_col;
1723 }
1724
Bram Moolenaar071d4272004-06-13 20:20:40 +00001725 return attr;
1726}
1727
1728/*
1729 * Get syntax attributes for current_lnum, current_col.
1730 */
1731 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001732syn_current_attr(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001733 int syncing, // When 1: called for syncing
1734 int displaying, // result will be displayed
1735 int *can_spell, // return: do spell checking
1736 int keep_state) // keep syntax stack afterwards
Bram Moolenaar071d4272004-06-13 20:20:40 +00001737{
1738 int syn_id;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001739 lpos_T endpos; // was: char_u *endp;
1740 lpos_T hl_startpos; // was: int hl_startcol;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001741 lpos_T hl_endpos;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001742 lpos_T eos_pos; // end-of-start match (start region)
1743 lpos_T eoe_pos; // end-of-end pattern
1744 int end_idx; // group ID for end pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00001745 int idx;
1746 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001747 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001748 int startcol;
1749 int endcol;
1750 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001751 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001752 short *next_list;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001753 int found_match; // found usable match
1754 static int try_next_column = FALSE; // must try in next col
Bram Moolenaar071d4272004-06-13 20:20:40 +00001755 int do_keywords;
1756 regmmatch_T regmatch;
1757 lpos_T pos;
1758 int lc_col;
1759 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001760 char_u buf_chartab[32]; // chartab array for syn iskyeyword
1761 char_u *line; // current line. NOTE: becomes invalid after
1762 // looking for a pattern match!
Bram Moolenaar071d4272004-06-13 20:20:40 +00001763
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001764 // variables for zero-width matches that have a "nextgroup" argument
Bram Moolenaar071d4272004-06-13 20:20:40 +00001765 int keep_next_list;
1766 int zero_width_next_list = FALSE;
1767 garray_T zero_width_next_ga;
1768
1769 /*
1770 * No character, no attributes! Past end of line?
1771 * Do try matching with an empty line (could be the start of a region).
1772 */
1773 line = syn_getcurline();
1774 if (line[current_col] == NUL && current_col != 0)
1775 {
1776 /*
1777 * If we found a match after the last column, use it.
1778 */
1779 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1780 && next_match_col != MAXCOL)
1781 (void)push_next_match(NULL);
1782
1783 current_finished = TRUE;
1784 current_state_stored = FALSE;
1785 return 0;
1786 }
1787
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001788 // if the current or next character is NUL, we will finish the line now
Bram Moolenaar071d4272004-06-13 20:20:40 +00001789 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1790 {
1791 current_finished = TRUE;
1792 current_state_stored = FALSE;
1793 }
1794
1795 /*
1796 * When in the previous column there was a match but it could not be used
1797 * (empty match or already matched in this column) need to try again in
1798 * the next column.
1799 */
1800 if (try_next_column)
1801 {
1802 next_match_idx = -1;
1803 try_next_column = FALSE;
1804 }
1805
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001806 // Only check for keywords when not syncing and there are some.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001807 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001808 && (syn_block->b_keywtab.ht_used > 0
1809 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001810
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001811 // Init the list of zero-width matches with a nextlist. This is used to
1812 // avoid matching the same item in the same position twice.
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001813 ga_init2(&zero_width_next_ga, sizeof(int), 10);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001814
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001815 // use syntax iskeyword option
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001816 save_chartab(buf_chartab);
1817
Bram Moolenaar071d4272004-06-13 20:20:40 +00001818 /*
1819 * Repeat matching keywords and patterns, to find contained items at the
1820 * same column. This stops when there are no extra matches at the current
1821 * column.
1822 */
1823 do
1824 {
1825 found_match = FALSE;
1826 keep_next_list = FALSE;
1827 syn_id = 0;
1828
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001829
Bram Moolenaar071d4272004-06-13 20:20:40 +00001830 /*
1831 * 1. Check for a current state.
1832 * Only when there is no current state, or if the current state may
1833 * contain other things, we need to check for keywords and patterns.
1834 * Always need to check for contained items if some item has the
1835 * "containedin" argument (takes extra time!).
1836 */
1837 if (current_state.ga_len)
1838 cur_si = &CUR_STATE(current_state.ga_len - 1);
1839 else
1840 cur_si = NULL;
1841
Bram Moolenaar860cae12010-06-05 23:22:07 +02001842 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001843 || cur_si->si_cont_list != NULL)
1844 {
1845 /*
1846 * 2. Check for keywords, if on a keyword char after a non-keyword
1847 * char. Don't do this when syncing.
1848 */
1849 if (do_keywords)
1850 {
1851 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001852 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001853 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001854 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00001855 - (has_mbyte
1856 ? (*mb_head_off)(line, line + current_col - 1)
Bram Moolenaar264b74f2019-01-24 17:18:42 +01001857 : 0) , syn_buf)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001858 {
1859 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001860 &endcol, &flags, &next_list, cur_si,
1861 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001862 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001863 {
1864 if (push_current_state(KEYWORD_IDX) == OK)
1865 {
1866 cur_si = &CUR_STATE(current_state.ga_len - 1);
1867 cur_si->si_m_startcol = current_col;
1868 cur_si->si_h_startpos.lnum = current_lnum;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001869 cur_si->si_h_startpos.col = 0; // starts right away
Bram Moolenaar071d4272004-06-13 20:20:40 +00001870 cur_si->si_m_endpos.lnum = current_lnum;
1871 cur_si->si_m_endpos.col = endcol;
1872 cur_si->si_h_endpos.lnum = current_lnum;
1873 cur_si->si_h_endpos.col = endcol;
1874 cur_si->si_ends = TRUE;
1875 cur_si->si_end_idx = 0;
1876 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001877#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02001878 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001879 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001880 if (current_state.ga_len > 1)
1881 cur_si->si_flags |=
1882 CUR_STATE(current_state.ga_len - 2).si_flags
1883 & HL_CONCEAL;
1884#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001885 cur_si->si_id = syn_id;
1886 cur_si->si_trans_id = syn_id;
1887 if (flags & HL_TRANSP)
1888 {
1889 if (current_state.ga_len < 2)
1890 {
1891 cur_si->si_attr = 0;
1892 cur_si->si_trans_id = 0;
1893 }
1894 else
1895 {
1896 cur_si->si_attr = CUR_STATE(
1897 current_state.ga_len - 2).si_attr;
1898 cur_si->si_trans_id = CUR_STATE(
1899 current_state.ga_len - 2).si_trans_id;
1900 }
1901 }
1902 else
1903 cur_si->si_attr = syn_id2attr(syn_id);
1904 cur_si->si_cont_list = NULL;
1905 cur_si->si_next_list = next_list;
1906 check_keepend();
1907 }
1908 else
1909 vim_free(next_list);
1910 }
1911 }
1912 }
1913
1914 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001915 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001916 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001917 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001918 {
1919 /*
1920 * If we didn't check for a match yet, or we are past it, check
1921 * for any match with a pattern.
1922 */
1923 if (next_match_idx < 0 || next_match_col < (int)current_col)
1924 {
1925 /*
1926 * Check all relevant patterns for a match at this
1927 * position. This is complicated, because matching with a
1928 * pattern takes quite a bit of time, thus we want to
1929 * avoid doing it when it's not needed.
1930 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001931 next_match_idx = 0; // no match in this line yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00001932 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001933 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001934 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001935 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001936 if ( spp->sp_syncing == syncing
1937 && (displaying || !(spp->sp_flags & HL_DISPLAY))
1938 && (spp->sp_type == SPTYPE_MATCH
1939 || spp->sp_type == SPTYPE_START)
1940 && (current_next_list != NULL
1941 ? in_id_list(NULL, current_next_list,
1942 &spp->sp_syn, 0)
1943 : (cur_si == NULL
1944 ? !(spp->sp_flags & HL_CONTAINED)
1945 : in_id_list(cur_si,
1946 cur_si->si_cont_list, &spp->sp_syn,
1947 spp->sp_flags & HL_CONTAINED))))
1948 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001949 int r;
1950
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001951 // If we already tried matching in this line, and
1952 // there isn't a match before next_match_col, skip
1953 // this item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001954 if (spp->sp_line_id == current_line_id
1955 && spp->sp_startcol >= next_match_col)
1956 continue;
1957 spp->sp_line_id = current_line_id;
1958
1959 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
1960 if (lc_col < 0)
1961 lc_col = 0;
1962
1963 regmatch.rmm_ic = spp->sp_ic;
1964 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001965 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001966 current_lnum,
1967 (colnr_T)lc_col,
Bram Moolenaard23a8232018-02-10 18:45:26 +01001968 IF_SYN_TIME(&spp->sp_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001969 spp->sp_prog = regmatch.regprog;
1970 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001971 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001972 // no match in this line, try another one
Bram Moolenaar071d4272004-06-13 20:20:40 +00001973 spp->sp_startcol = MAXCOL;
1974 continue;
1975 }
1976
1977 /*
1978 * Compute the first column of the match.
1979 */
1980 syn_add_start_off(&pos, &regmatch,
1981 spp, SPO_MS_OFF, -1);
1982 if (pos.lnum > current_lnum)
1983 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001984 // must have used end of match in a next line,
1985 // we can't handle that
Bram Moolenaar071d4272004-06-13 20:20:40 +00001986 spp->sp_startcol = MAXCOL;
1987 continue;
1988 }
1989 startcol = pos.col;
1990
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001991 // remember the next column where this pattern
1992 // matches in the current line
Bram Moolenaar071d4272004-06-13 20:20:40 +00001993 spp->sp_startcol = startcol;
1994
1995 /*
1996 * If a previously found match starts at a lower
1997 * column number, don't use this one.
1998 */
1999 if (startcol >= next_match_col)
2000 continue;
2001
2002 /*
2003 * If we matched this pattern at this position
2004 * before, skip it. Must retry in the next
2005 * column, because it may match from there.
2006 */
2007 if (did_match_already(idx, &zero_width_next_ga))
2008 {
2009 try_next_column = TRUE;
2010 continue;
2011 }
2012
2013 endpos.lnum = regmatch.endpos[0].lnum;
2014 endpos.col = regmatch.endpos[0].col;
2015
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002016 // Compute the highlight start.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002017 syn_add_start_off(&hl_startpos, &regmatch,
2018 spp, SPO_HS_OFF, -1);
2019
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002020 // Compute the region start.
2021 // Default is to use the end of the match.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002022 syn_add_end_off(&eos_pos, &regmatch,
2023 spp, SPO_RS_OFF, 0);
2024
2025 /*
2026 * Grab the external submatches before they get
2027 * overwritten. Reference count doesn't change.
2028 */
2029 unref_extmatch(cur_extmatch);
2030 cur_extmatch = re_extmatch_out;
2031 re_extmatch_out = NULL;
2032
2033 flags = 0;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002034 eoe_pos.lnum = 0; // avoid warning
Bram Moolenaar071d4272004-06-13 20:20:40 +00002035 eoe_pos.col = 0;
2036 end_idx = 0;
2037 hl_endpos.lnum = 0;
2038
2039 /*
2040 * For a "oneline" the end must be found in the
2041 * same line too. Search for it after the end of
2042 * the match with the start pattern. Set the
2043 * resulting end positions at the same time.
2044 */
2045 if (spp->sp_type == SPTYPE_START
2046 && (spp->sp_flags & HL_ONELINE))
2047 {
2048 lpos_T startpos;
2049
2050 startpos = endpos;
2051 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2052 &flags, &eoe_pos, &end_idx, cur_extmatch);
2053 if (endpos.lnum == 0)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002054 continue; // not found
Bram Moolenaar071d4272004-06-13 20:20:40 +00002055 }
2056
2057 /*
2058 * For a "match" the size must be > 0 after the
2059 * end offset needs has been added. Except when
2060 * syncing.
2061 */
2062 else if (spp->sp_type == SPTYPE_MATCH)
2063 {
2064 syn_add_end_off(&hl_endpos, &regmatch, spp,
2065 SPO_HE_OFF, 0);
2066 syn_add_end_off(&endpos, &regmatch, spp,
2067 SPO_ME_OFF, 0);
2068 if (endpos.lnum == current_lnum
2069 && (int)endpos.col + syncing < startcol)
2070 {
2071 /*
2072 * If an empty string is matched, may need
2073 * to try matching again at next column.
2074 */
2075 if (regmatch.startpos[0].col
2076 == regmatch.endpos[0].col)
2077 try_next_column = TRUE;
2078 continue;
2079 }
2080 }
2081
2082 /*
2083 * keep the best match so far in next_match_*
2084 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002085 // Highlighting must start after startpos and end
2086 // before endpos.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002087 if (hl_startpos.lnum == current_lnum
2088 && (int)hl_startpos.col < startcol)
2089 hl_startpos.col = startcol;
2090 limit_pos_zero(&hl_endpos, &endpos);
2091
2092 next_match_idx = idx;
2093 next_match_col = startcol;
2094 next_match_m_endpos = endpos;
2095 next_match_h_endpos = hl_endpos;
2096 next_match_h_startpos = hl_startpos;
2097 next_match_flags = flags;
2098 next_match_eos_pos = eos_pos;
2099 next_match_eoe_pos = eoe_pos;
2100 next_match_end_idx = end_idx;
2101 unref_extmatch(next_match_extmatch);
2102 next_match_extmatch = cur_extmatch;
2103 cur_extmatch = NULL;
2104 }
2105 }
2106 }
2107
2108 /*
2109 * If we found a match at the current column, use it.
2110 */
2111 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2112 {
2113 synpat_T *lspp;
2114
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002115 // When a zero-width item matched which has a nextgroup,
2116 // don't push the item but set nextgroup.
Bram Moolenaar860cae12010-06-05 23:22:07 +02002117 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002118 if (next_match_m_endpos.lnum == current_lnum
2119 && next_match_m_endpos.col == current_col
2120 && lspp->sp_next_list != NULL)
2121 {
2122 current_next_list = lspp->sp_next_list;
2123 current_next_flags = lspp->sp_flags;
2124 keep_next_list = TRUE;
2125 zero_width_next_list = TRUE;
2126
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002127 // Add the index to a list, so that we can check
2128 // later that we don't match it again (and cause an
2129 // endless loop).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002130 if (ga_grow(&zero_width_next_ga, 1) == OK)
2131 {
2132 ((int *)(zero_width_next_ga.ga_data))
2133 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002134 }
2135 next_match_idx = -1;
2136 }
2137 else
2138 cur_si = push_next_match(cur_si);
2139 found_match = TRUE;
2140 }
2141 }
2142 }
2143
2144 /*
2145 * Handle searching for nextgroup match.
2146 */
2147 if (current_next_list != NULL && !keep_next_list)
2148 {
2149 /*
2150 * If a nextgroup was not found, continue looking for one if:
2151 * - this is an empty line and the "skipempty" option was given
2152 * - we are on white space and the "skipwhite" option was given
2153 */
2154 if (!found_match)
2155 {
2156 line = syn_getcurline();
2157 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002158 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002159 || ((current_next_flags & HL_SKIPEMPTY)
2160 && *line == NUL))
2161 break;
2162 }
2163
2164 /*
2165 * If a nextgroup was found: Use it, and continue looking for
2166 * contained matches.
2167 * If a nextgroup was not found: Continue looking for a normal
2168 * match.
2169 * When did set current_next_list for a zero-width item and no
2170 * match was found don't loop (would get stuck).
2171 */
2172 current_next_list = NULL;
2173 next_match_idx = -1;
2174 if (!zero_width_next_list)
2175 found_match = TRUE;
2176 }
2177
2178 } while (found_match);
2179
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002180 restore_chartab(buf_chartab);
2181
Bram Moolenaar071d4272004-06-13 20:20:40 +00002182 /*
2183 * Use attributes from the current state, if within its highlighting.
2184 * If not, use attributes from the current-but-one state, etc.
2185 */
2186 current_attr = 0;
2187#ifdef FEAT_EVAL
2188 current_id = 0;
2189 current_trans_id = 0;
2190#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002191#ifdef FEAT_CONCEAL
2192 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02002193 current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002194#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002195 if (cur_si != NULL)
2196 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002197#ifndef FEAT_EVAL
2198 int current_trans_id = 0;
2199#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002200 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2201 {
2202 sip = &CUR_STATE(idx);
2203 if ((current_lnum > sip->si_h_startpos.lnum
2204 || (current_lnum == sip->si_h_startpos.lnum
2205 && current_col >= sip->si_h_startpos.col))
2206 && (sip->si_h_endpos.lnum == 0
2207 || current_lnum < sip->si_h_endpos.lnum
2208 || (current_lnum == sip->si_h_endpos.lnum
2209 && current_col < sip->si_h_endpos.col)))
2210 {
2211 current_attr = sip->si_attr;
2212#ifdef FEAT_EVAL
2213 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002214#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002215 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002216#ifdef FEAT_CONCEAL
2217 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002218 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002219 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002220#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002221 break;
2222 }
2223 }
2224
Bram Moolenaar217ad922005-03-20 22:37:15 +00002225 if (can_spell != NULL)
2226 {
2227 struct sp_syn sps;
2228
2229 /*
2230 * set "can_spell" to TRUE if spell checking is supposed to be
2231 * done in the current item.
2232 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002233 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002234 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002235 // There is no @Spell cluster: Do spelling for items without
2236 // @NoSpell cluster.
Bram Moolenaar860cae12010-06-05 23:22:07 +02002237 if (syn_block->b_nospell_cluster_id == 0
2238 || current_trans_id == 0)
2239 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002240 else
2241 {
2242 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002243 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002244 sps.cont_in_list = NULL;
2245 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2246 }
2247 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002248 else
2249 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002250 // The @Spell cluster is defined: Do spelling in items with
2251 // the @Spell cluster. But not when @NoSpell is also there.
2252 // At the toplevel only spell check when ":syn spell toplevel"
2253 // was used.
Bram Moolenaar3638c682005-06-08 22:05:14 +00002254 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002255 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002256 else
2257 {
2258 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002259 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002260 sps.cont_in_list = NULL;
2261 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2262
Bram Moolenaar860cae12010-06-05 23:22:07 +02002263 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002264 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002265 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002266 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2267 *can_spell = FALSE;
2268 }
2269 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002270 }
2271 }
2272
2273
Bram Moolenaar071d4272004-06-13 20:20:40 +00002274 /*
2275 * Check for end of current state (and the states before it) at the
2276 * next column. Don't do this for syncing, because we would miss a
2277 * single character match.
2278 * First check if the current state ends at the current column. It
2279 * may be for an empty match and a containing item might end in the
2280 * current column.
2281 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002282 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002283 {
2284 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002285 if (current_state.ga_len > 0
2286 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002287 {
2288 ++current_col;
2289 check_state_ends();
2290 --current_col;
2291 }
2292 }
2293 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002294 else if (can_spell != NULL)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002295 // Default: Only do spelling when there is no @Spell cluster or when
2296 // ":syn spell toplevel" was used.
Bram Moolenaar860cae12010-06-05 23:22:07 +02002297 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2298 ? (syn_block->b_spell_cluster_id == 0)
2299 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002300
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002301 // nextgroup ends at end of line, unless "skipnl" or "skipempty" present
Bram Moolenaar071d4272004-06-13 20:20:40 +00002302 if (current_next_list != NULL
Bram Moolenaar069dafc2018-03-03 20:02:19 +01002303 && (line = syn_getcurline())[current_col] != NUL
2304 && line[current_col + 1] == NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002305 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2306 current_next_list = NULL;
2307
2308 if (zero_width_next_ga.ga_len > 0)
2309 ga_clear(&zero_width_next_ga);
2310
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002311 // No longer need external matches. But keep next_match_extmatch.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002312 unref_extmatch(re_extmatch_out);
2313 re_extmatch_out = NULL;
2314 unref_extmatch(cur_extmatch);
2315
2316 return current_attr;
2317}
2318
2319
2320/*
2321 * Check if we already matched pattern "idx" at the current column.
2322 */
2323 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002324did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002325{
2326 int i;
2327
2328 for (i = current_state.ga_len; --i >= 0; )
2329 if (CUR_STATE(i).si_m_startcol == (int)current_col
2330 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2331 && CUR_STATE(i).si_idx == idx)
2332 return TRUE;
2333
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002334 // Zero-width matches with a nextgroup argument are not put on the syntax
2335 // stack, and can only be matched once anyway.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002336 for (i = gap->ga_len; --i >= 0; )
2337 if (((int *)(gap->ga_data))[i] == idx)
2338 return TRUE;
2339
2340 return FALSE;
2341}
2342
2343/*
2344 * Push the next match onto the stack.
2345 */
2346 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002347push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002348{
2349 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002350#ifdef FEAT_CONCEAL
2351 int save_flags;
2352#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002353
Bram Moolenaar860cae12010-06-05 23:22:07 +02002354 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002355
2356 /*
2357 * Push the item in current_state stack;
2358 */
2359 if (push_current_state(next_match_idx) == OK)
2360 {
2361 /*
2362 * If it's a start-skip-end type that crosses lines, figure out how
2363 * much it continues in this line. Otherwise just fill in the length.
2364 */
2365 cur_si = &CUR_STATE(current_state.ga_len - 1);
2366 cur_si->si_h_startpos = next_match_h_startpos;
2367 cur_si->si_m_startcol = current_col;
2368 cur_si->si_m_lnum = current_lnum;
2369 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002370#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002371 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002372 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002373 if (current_state.ga_len > 1)
2374 cur_si->si_flags |=
2375 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2376#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002377 cur_si->si_next_list = spp->sp_next_list;
2378 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2379 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2380 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002381 // Try to find the end pattern in the current line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002382 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2383 check_keepend();
2384 }
2385 else
2386 {
2387 cur_si->si_m_endpos = next_match_m_endpos;
2388 cur_si->si_h_endpos = next_match_h_endpos;
2389 cur_si->si_ends = TRUE;
2390 cur_si->si_flags |= next_match_flags;
2391 cur_si->si_eoe_pos = next_match_eoe_pos;
2392 cur_si->si_end_idx = next_match_end_idx;
2393 }
2394 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2395 keepend_level = current_state.ga_len - 1;
2396 check_keepend();
2397 update_si_attr(current_state.ga_len - 1);
2398
Bram Moolenaar860cae12010-06-05 23:22:07 +02002399#ifdef FEAT_CONCEAL
2400 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2401#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002402 /*
2403 * If the start pattern has another highlight group, push another item
2404 * on the stack for the start pattern.
2405 */
2406 if ( spp->sp_type == SPTYPE_START
2407 && spp->sp_syn_match_id != 0
2408 && push_current_state(next_match_idx) == OK)
2409 {
2410 cur_si = &CUR_STATE(current_state.ga_len - 1);
2411 cur_si->si_h_startpos = next_match_h_startpos;
2412 cur_si->si_m_startcol = current_col;
2413 cur_si->si_m_lnum = current_lnum;
2414 cur_si->si_m_endpos = next_match_eos_pos;
2415 cur_si->si_h_endpos = next_match_eos_pos;
2416 cur_si->si_ends = TRUE;
2417 cur_si->si_end_idx = 0;
2418 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002419#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002420 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002421 cur_si->si_flags |= save_flags;
2422 if (cur_si->si_flags & HL_CONCEALENDS)
2423 cur_si->si_flags |= HL_CONCEAL;
2424#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002425 cur_si->si_next_list = NULL;
2426 check_keepend();
2427 update_si_attr(current_state.ga_len - 1);
2428 }
2429 }
2430
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002431 next_match_idx = -1; // try other match next time
Bram Moolenaar071d4272004-06-13 20:20:40 +00002432
2433 return cur_si;
2434}
2435
2436/*
2437 * Check for end of current state (and the states before it).
2438 */
2439 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002440check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002441{
2442 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002443 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002444
2445 cur_si = &CUR_STATE(current_state.ga_len - 1);
2446 for (;;)
2447 {
2448 if (cur_si->si_ends
2449 && (cur_si->si_m_endpos.lnum < current_lnum
2450 || (cur_si->si_m_endpos.lnum == current_lnum
2451 && cur_si->si_m_endpos.col <= current_col)))
2452 {
2453 /*
2454 * If there is an end pattern group ID, highlight the end pattern
2455 * now. No need to pop the current item from the stack.
2456 * Only do this if the end pattern continues beyond the current
2457 * position.
2458 */
2459 if (cur_si->si_end_idx
2460 && (cur_si->si_eoe_pos.lnum > current_lnum
2461 || (cur_si->si_eoe_pos.lnum == current_lnum
2462 && cur_si->si_eoe_pos.col > current_col)))
2463 {
2464 cur_si->si_idx = cur_si->si_end_idx;
2465 cur_si->si_end_idx = 0;
2466 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2467 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2468 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002469#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002470 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002471 if (cur_si->si_flags & HL_CONCEALENDS)
2472 cur_si->si_flags |= HL_CONCEAL;
2473#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002474 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002475
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002476 // nextgroup= should not match in the end pattern
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002477 current_next_list = NULL;
2478
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002479 // what matches next may be different now, clear it
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002480 next_match_idx = 0;
2481 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002482 break;
2483 }
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002484
2485 // handle next_list, unless at end of line and no "skipnl" or
2486 // "skipempty"
2487 current_next_list = cur_si->si_next_list;
2488 current_next_flags = cur_si->si_flags;
2489 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2490 && syn_getcurline()[current_col] == NUL)
2491 current_next_list = NULL;
2492
2493 // When the ended item has "extend", another item with
2494 // "keepend" now needs to check for its end.
2495 had_extend = (cur_si->si_flags & HL_EXTEND);
2496
2497 pop_current_state();
2498
2499 if (current_state.ga_len == 0)
2500 break;
2501
2502 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002503 {
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002504 syn_update_ends(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002505 if (current_state.ga_len == 0)
2506 break;
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002507 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002508
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002509 cur_si = &CUR_STATE(current_state.ga_len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002510
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002511 /*
2512 * Only for a region the search for the end continues after
2513 * the end of the contained item. If the contained match
2514 * included the end-of-line, break here, the region continues.
2515 * Don't do this when:
2516 * - "keepend" is used for the contained item
2517 * - not at the end of the line (could be end="x$"me=e-1).
2518 * - "excludenl" is used (HL_HAS_EOL won't be set)
2519 */
2520 if (cur_si->si_idx >= 0
2521 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
2522 == SPTYPE_START
2523 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2524 {
2525 update_si_end(cur_si, (int)current_col, TRUE);
2526 check_keepend();
2527 if ((current_next_flags & HL_HAS_EOL)
2528 && keepend_level < 0
2529 && syn_getcurline()[current_col] == NUL)
2530 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002531 }
2532 }
2533 else
2534 break;
2535 }
2536}
2537
2538/*
2539 * Update an entry in the current_state stack for a match or region. This
2540 * fills in si_attr, si_next_list and si_cont_list.
2541 */
2542 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002543update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002544{
2545 stateitem_T *sip = &CUR_STATE(idx);
2546 synpat_T *spp;
2547
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002548 // This should not happen...
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002549 if (sip->si_idx < 0)
2550 return;
2551
Bram Moolenaar860cae12010-06-05 23:22:07 +02002552 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002553 if (sip->si_flags & HL_MATCH)
2554 sip->si_id = spp->sp_syn_match_id;
2555 else
2556 sip->si_id = spp->sp_syn.id;
2557 sip->si_attr = syn_id2attr(sip->si_id);
2558 sip->si_trans_id = sip->si_id;
2559 if (sip->si_flags & HL_MATCH)
2560 sip->si_cont_list = NULL;
2561 else
2562 sip->si_cont_list = spp->sp_cont_list;
2563
2564 /*
2565 * For transparent items, take attr from outer item.
2566 * Also take cont_list, if there is none.
2567 * Don't do this for the matchgroup of a start or end pattern.
2568 */
2569 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2570 {
2571 if (idx == 0)
2572 {
2573 sip->si_attr = 0;
2574 sip->si_trans_id = 0;
2575 if (sip->si_cont_list == NULL)
2576 sip->si_cont_list = ID_LIST_ALL;
2577 }
2578 else
2579 {
2580 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2581 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
2582 if (sip->si_cont_list == NULL)
2583 {
2584 sip->si_flags |= HL_TRANS_CONT;
2585 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2586 }
2587 }
2588 }
2589}
2590
2591/*
2592 * Check the current stack for patterns with "keepend" flag.
2593 * Propagate the match-end to contained items, until a "skipend" item is found.
2594 */
2595 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002596check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002597{
2598 int i;
2599 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002600 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002601 stateitem_T *sip;
2602
2603 /*
2604 * This check can consume a lot of time; only do it from the level where
2605 * there really is a keepend.
2606 */
2607 if (keepend_level < 0)
2608 return;
2609
2610 /*
2611 * Find the last index of an "extend" item. "keepend" items before that
2612 * won't do anything. If there is no "extend" item "i" will be
2613 * "keepend_level" and all "keepend" items will work normally.
2614 */
2615 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2616 if (CUR_STATE(i).si_flags & HL_EXTEND)
2617 break;
2618
2619 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002620 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002621 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002622 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002623 for ( ; i < current_state.ga_len; ++i)
2624 {
2625 sip = &CUR_STATE(i);
2626 if (maxpos.lnum != 0)
2627 {
2628 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002629 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002630 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2631 sip->si_ends = TRUE;
2632 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002633 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2634 {
2635 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002636 || maxpos.lnum > sip->si_m_endpos.lnum
2637 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002638 && maxpos.col > sip->si_m_endpos.col))
2639 maxpos = sip->si_m_endpos;
2640 if (maxpos_h.lnum == 0
2641 || maxpos_h.lnum > sip->si_h_endpos.lnum
2642 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2643 && maxpos_h.col > sip->si_h_endpos.col))
2644 maxpos_h = sip->si_h_endpos;
2645 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002646 }
2647}
2648
2649/*
2650 * Update an entry in the current_state stack for a start-skip-end pattern.
2651 * This finds the end of the current item, if it's in the current line.
2652 *
2653 * Return the flags for the matched END.
2654 */
2655 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002656update_si_end(
2657 stateitem_T *sip,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002658 int startcol, // where to start searching for the end
2659 int force) // when TRUE overrule a previous end
Bram Moolenaar071d4272004-06-13 20:20:40 +00002660{
2661 lpos_T startpos;
2662 lpos_T endpos;
2663 lpos_T hl_endpos;
2664 lpos_T end_endpos;
2665 int end_idx;
2666
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002667 // return quickly for a keyword
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002668 if (sip->si_idx < 0)
2669 return;
2670
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002671 // Don't update when it's already done. Can be a match of an end pattern
2672 // that started in a previous line. Watch out: can also be a "keepend"
2673 // from a containing item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002674 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2675 return;
2676
2677 /*
2678 * We need to find the end of the region. It may continue in the next
2679 * line.
2680 */
2681 end_idx = 0;
2682 startpos.lnum = current_lnum;
2683 startpos.col = startcol;
2684 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2685 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2686
2687 if (endpos.lnum == 0)
2688 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002689 // No end pattern matched.
Bram Moolenaar860cae12010-06-05 23:22:07 +02002690 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002691 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002692 // a "oneline" never continues in the next line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002693 sip->si_ends = TRUE;
2694 sip->si_m_endpos.lnum = current_lnum;
2695 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2696 }
2697 else
2698 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002699 // continues in the next line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002700 sip->si_ends = FALSE;
2701 sip->si_m_endpos.lnum = 0;
2702 }
2703 sip->si_h_endpos = sip->si_m_endpos;
2704 }
2705 else
2706 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002707 // match within this line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002708 sip->si_m_endpos = endpos;
2709 sip->si_h_endpos = hl_endpos;
2710 sip->si_eoe_pos = end_endpos;
2711 sip->si_ends = TRUE;
2712 sip->si_end_idx = end_idx;
2713 }
2714}
2715
2716/*
2717 * Add a new state to the current state stack.
2718 * It is cleared and the index set to "idx".
2719 * Return FAIL if it's not possible (out of memory).
2720 */
2721 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002722push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002723{
2724 if (ga_grow(&current_state, 1) == FAIL)
2725 return FAIL;
Bram Moolenaara80faa82020-04-12 19:37:17 +02002726 CLEAR_POINTER(&CUR_STATE(current_state.ga_len));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002727 CUR_STATE(current_state.ga_len).si_idx = idx;
2728 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002729 return OK;
2730}
2731
2732/*
2733 * Remove a state from the current_state stack.
2734 */
2735 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002736pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002737{
2738 if (current_state.ga_len)
2739 {
2740 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2741 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002742 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002743 // after the end of a pattern, try matching a keyword or pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00002744 next_match_idx = -1;
2745
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002746 // if first state with "keepend" is popped, reset keepend_level
Bram Moolenaar071d4272004-06-13 20:20:40 +00002747 if (keepend_level >= current_state.ga_len)
2748 keepend_level = -1;
2749}
2750
2751/*
2752 * Find the end of a start/skip/end syntax region after "startpos".
2753 * Only checks one line.
2754 * Also handles a match item that continued from a previous line.
2755 * If not found, the syntax item continues in the next line. m_endpos->lnum
2756 * will be 0.
2757 * If found, the end of the region and the end of the highlighting is
2758 * computed.
2759 */
2760 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002761find_endpos(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002762 int idx, // index of the pattern
2763 lpos_T *startpos, // where to start looking for an END match
2764 lpos_T *m_endpos, // return: end of match
2765 lpos_T *hl_endpos, // return: end of highlighting
2766 long *flagsp, // return: flags of matching END
2767 lpos_T *end_endpos, // return: end of end pattern match
2768 int *end_idx, // return: group ID for end pat. match, or 0
2769 reg_extmatch_T *start_ext) // submatches from the start pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00002770{
2771 colnr_T matchcol;
2772 synpat_T *spp, *spp_skip;
2773 int start_idx;
2774 int best_idx;
2775 regmmatch_T regmatch;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002776 regmmatch_T best_regmatch; // startpos/endpos of best match
Bram Moolenaar071d4272004-06-13 20:20:40 +00002777 lpos_T pos;
2778 char_u *line;
2779 int had_match = FALSE;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002780 char_u buf_chartab[32]; // chartab array for syn option iskyeyword
Bram Moolenaar071d4272004-06-13 20:20:40 +00002781
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002782 // just in case we are invoked for a keyword
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002783 if (idx < 0)
2784 return;
2785
Bram Moolenaar071d4272004-06-13 20:20:40 +00002786 /*
2787 * Check for being called with a START pattern.
2788 * Can happen with a match that continues to the next line, because it
2789 * contained a region.
2790 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002791 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002792 if (spp->sp_type != SPTYPE_START)
2793 {
2794 *hl_endpos = *startpos;
2795 return;
2796 }
2797
2798 /*
2799 * Find the SKIP or first END pattern after the last START pattern.
2800 */
2801 for (;;)
2802 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002803 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002804 if (spp->sp_type != SPTYPE_START)
2805 break;
2806 ++idx;
2807 }
2808
2809 /*
2810 * Lookup the SKIP pattern (if present)
2811 */
2812 if (spp->sp_type == SPTYPE_SKIP)
2813 {
2814 spp_skip = spp;
2815 ++idx;
2816 }
2817 else
2818 spp_skip = NULL;
2819
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002820 // Setup external matches for syn_regexec().
Bram Moolenaar071d4272004-06-13 20:20:40 +00002821 unref_extmatch(re_extmatch_in);
2822 re_extmatch_in = ref_extmatch(start_ext);
2823
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002824 matchcol = startpos->col; // start looking for a match at sstart
2825 start_idx = idx; // remember the first END pattern.
2826 best_regmatch.startpos[0].col = 0; // avoid compiler warning
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002827
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002828 // use syntax iskeyword option
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002829 save_chartab(buf_chartab);
2830
Bram Moolenaar071d4272004-06-13 20:20:40 +00002831 for (;;)
2832 {
2833 /*
2834 * Find end pattern that matches first after "matchcol".
2835 */
2836 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002837 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002838 {
2839 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002840 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002841
Bram Moolenaar860cae12010-06-05 23:22:07 +02002842 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002843 if (spp->sp_type != SPTYPE_END) // past last END pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00002844 break;
2845 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2846 if (lc_col < 0)
2847 lc_col = 0;
2848
2849 regmatch.rmm_ic = spp->sp_ic;
2850 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002851 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
2852 IF_SYN_TIME(&spp->sp_time));
2853 spp->sp_prog = regmatch.regprog;
2854 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002855 {
2856 if (best_idx == -1 || regmatch.startpos[0].col
2857 < best_regmatch.startpos[0].col)
2858 {
2859 best_idx = idx;
2860 best_regmatch.startpos[0] = regmatch.startpos[0];
2861 best_regmatch.endpos[0] = regmatch.endpos[0];
2862 }
2863 }
2864 }
2865
2866 /*
2867 * If all end patterns have been tried, and there is no match, the
2868 * item continues until end-of-line.
2869 */
2870 if (best_idx == -1)
2871 break;
2872
2873 /*
2874 * If the skip pattern matches before the end pattern,
2875 * continue searching after the skip pattern.
2876 */
2877 if (spp_skip != NULL)
2878 {
2879 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002880 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002881
2882 if (lc_col < 0)
2883 lc_col = 0;
2884 regmatch.rmm_ic = spp_skip->sp_ic;
2885 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002886 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
2887 IF_SYN_TIME(&spp_skip->sp_time));
2888 spp_skip->sp_prog = regmatch.regprog;
2889 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00002890 <= best_regmatch.startpos[0].col)
2891 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01002892 int line_len;
2893
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002894 // Add offset to skip pattern match
Bram Moolenaar071d4272004-06-13 20:20:40 +00002895 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2896
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002897 // If the skip pattern goes on to the next line, there is no
2898 // match with an end pattern in this line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002899 if (pos.lnum > startpos->lnum)
2900 break;
2901
2902 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01002903 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002904
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002905 // take care of an empty match or negative offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00002906 if (pos.col <= matchcol)
2907 ++matchcol;
2908 else if (pos.col <= regmatch.endpos[0].col)
2909 matchcol = pos.col;
2910 else
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002911 // Be careful not to jump over the NUL at the end-of-line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002912 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01002913 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002914 ++matchcol)
2915 ;
2916
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002917 // if the skip pattern includes end-of-line, break here
Bram Moolenaar04bff882016-01-05 20:46:16 +01002918 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002919 break;
2920
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002921 continue; // start with first end pattern again
Bram Moolenaar071d4272004-06-13 20:20:40 +00002922 }
2923 }
2924
2925 /*
2926 * Match from start pattern to end pattern.
2927 * Correct for match and highlight offset of end pattern.
2928 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002929 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002930 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002931 // can't end before the start
Bram Moolenaar071d4272004-06-13 20:20:40 +00002932 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
2933 m_endpos->col = startpos->col;
2934
2935 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002936 // can't end before the start
Bram Moolenaar071d4272004-06-13 20:20:40 +00002937 if (end_endpos->lnum == startpos->lnum
2938 && end_endpos->col < startpos->col)
2939 end_endpos->col = startpos->col;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002940 // can't end after the match
Bram Moolenaar071d4272004-06-13 20:20:40 +00002941 limit_pos(end_endpos, m_endpos);
2942
2943 /*
2944 * If the end group is highlighted differently, adjust the pointers.
2945 */
2946 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
2947 {
2948 *end_idx = best_idx;
2949 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
2950 {
2951 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
2952 hl_endpos->col = best_regmatch.endpos[0].col;
2953 }
2954 else
2955 {
2956 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
2957 hl_endpos->col = best_regmatch.startpos[0].col;
2958 }
2959 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
2960
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002961 // can't end before the start
Bram Moolenaar071d4272004-06-13 20:20:40 +00002962 if (hl_endpos->lnum == startpos->lnum
2963 && hl_endpos->col < startpos->col)
2964 hl_endpos->col = startpos->col;
2965 limit_pos(hl_endpos, m_endpos);
2966
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002967 // now the match ends where the highlighting ends, it is turned
2968 // into the matchgroup for the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00002969 *m_endpos = *hl_endpos;
2970 }
2971 else
2972 {
2973 *end_idx = 0;
2974 *hl_endpos = *end_endpos;
2975 }
2976
2977 *flagsp = spp->sp_flags;
2978
2979 had_match = TRUE;
2980 break;
2981 }
2982
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002983 // no match for an END pattern in this line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002984 if (!had_match)
2985 m_endpos->lnum = 0;
2986
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002987 restore_chartab(buf_chartab);
2988
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002989 // Remove external matches.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002990 unref_extmatch(re_extmatch_in);
2991 re_extmatch_in = NULL;
2992}
2993
2994/*
2995 * Limit "pos" not to be after "limit".
2996 */
2997 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002998limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002999{
3000 if (pos->lnum > limit->lnum)
3001 *pos = *limit;
3002 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3003 pos->col = limit->col;
3004}
3005
3006/*
3007 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3008 */
3009 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003010limit_pos_zero(
3011 lpos_T *pos,
3012 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003013{
3014 if (pos->lnum == 0)
3015 *pos = *limit;
3016 else
3017 limit_pos(pos, limit);
3018}
3019
3020/*
3021 * Add offset to matched text for end of match or highlight.
3022 */
3023 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003024syn_add_end_off(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003025 lpos_T *result, // returned position
3026 regmmatch_T *regmatch, // start/end of match
3027 synpat_T *spp, // matched pattern
3028 int idx, // index of offset
3029 int extra) // extra chars for offset to start
Bram Moolenaar071d4272004-06-13 20:20:40 +00003030{
3031 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003032 int off;
3033 char_u *base;
3034 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003035
3036 if (spp->sp_off_flags & (1 << idx))
3037 {
3038 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003039 col = regmatch->startpos[0].col;
3040 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003041 }
3042 else
3043 {
3044 result->lnum = regmatch->endpos[0].lnum;
3045 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003046 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003047 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003048 // Don't go past the end of the line. Matters for "rs=e+2" when there
3049 // is a matchgroup. Watch out for match with last NL in the buffer.
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003050 if (result->lnum > syn_buf->b_ml.ml_line_count)
3051 col = 0;
3052 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003053 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003054 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3055 p = base + col;
3056 if (off > 0)
3057 {
3058 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003059 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003060 }
3061 else if (off < 0)
3062 {
3063 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003064 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003065 }
3066 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003067 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003068 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003069}
3070
3071/*
3072 * Add offset to matched text for start of match or highlight.
3073 * Avoid resulting column to become negative.
3074 */
3075 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003076syn_add_start_off(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003077 lpos_T *result, // returned position
3078 regmmatch_T *regmatch, // start/end of match
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003079 synpat_T *spp,
3080 int idx,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003081 int extra) // extra chars for offset to end
Bram Moolenaar071d4272004-06-13 20:20:40 +00003082{
3083 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003084 int off;
3085 char_u *base;
3086 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003087
3088 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3089 {
3090 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003091 col = regmatch->endpos[0].col;
3092 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003093 }
3094 else
3095 {
3096 result->lnum = regmatch->startpos[0].lnum;
3097 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003098 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003099 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003100 if (result->lnum > syn_buf->b_ml.ml_line_count)
3101 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003102 // a "\n" at the end of the pattern may take us below the last line
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003103 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003104 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003105 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003106 if (off != 0)
3107 {
3108 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3109 p = base + col;
3110 if (off > 0)
3111 {
3112 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003113 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003114 }
3115 else if (off < 0)
3116 {
3117 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003118 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003119 }
3120 col = (int)(p - base);
3121 }
3122 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003123}
3124
3125/*
3126 * Get current line in syntax buffer.
3127 */
3128 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003129syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003130{
3131 return ml_get_buf(syn_buf, current_lnum, FALSE);
3132}
3133
3134/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003135 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003136 * Returns TRUE when there is a match.
3137 */
3138 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003139syn_regexec(
3140 regmmatch_T *rmp,
3141 linenr_T lnum,
3142 colnr_T col,
3143 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003144{
Bram Moolenaar6f0cf622022-06-19 12:27:45 +01003145 int r;
3146 int timed_out = FALSE;
Bram Moolenaarf7512552013-06-06 14:55:19 +02003147#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003148 proftime_T pt;
3149
3150 if (syn_time_on)
3151 profile_start(&pt);
3152#endif
3153
Bram Moolenaarbcf94422018-06-23 14:21:42 +02003154 if (rmp->regprog == NULL)
3155 // This can happen if a previous call to vim_regexec_multi() tried to
3156 // use the NFA engine, which resulted in NFA_TOO_EXPENSIVE, and
3157 // compiling the pattern with the other engine fails.
3158 return FALSE;
3159
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003160 rmp->rmm_maxcol = syn_buf->b_p_smc;
Paul Ollis65745772022-06-05 16:55:54 +01003161 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, &timed_out);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003162
Bram Moolenaarf7512552013-06-06 14:55:19 +02003163#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003164 if (syn_time_on)
3165 {
3166 profile_end(&pt);
3167 profile_add(&st->total, &pt);
3168 if (profile_cmp(&pt, &st->slowest) < 0)
3169 st->slowest = pt;
3170 ++st->count;
3171 if (r > 0)
3172 ++st->match;
3173 }
3174#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003175#ifdef FEAT_RELTIME
Bram Moolenaar6f0cf622022-06-19 12:27:45 +01003176 if (timed_out && redrawtime_limit_set && !syn_win->w_s->b_syn_slow)
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003177 {
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003178 syn_win->w_s->b_syn_slow = TRUE;
Bram Moolenaar32526b32019-01-19 17:43:09 +01003179 msg(_("'redrawtime' exceeded, syntax highlighting disabled"));
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003180 }
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003181#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003182
3183 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003184 {
3185 rmp->startpos[0].lnum += lnum;
3186 rmp->endpos[0].lnum += lnum;
3187 return TRUE;
3188 }
3189 return FALSE;
3190}
3191
3192/*
3193 * Check one position in a line for a matching keyword.
3194 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003195 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003196 */
3197 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003198check_keyword_id(
3199 char_u *line,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003200 int startcol, // position in line to check for keyword
3201 int *endcolp, // return: character after found keyword
3202 long *flagsp, // return: flags of matching keyword
3203 short **next_listp, // return: next_list of matching keyword
3204 stateitem_T *cur_si, // item at the top of the stack
3205 int *ccharp UNUSED) // conceal substitution char
Bram Moolenaar071d4272004-06-13 20:20:40 +00003206{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003207 keyentry_T *kp;
3208 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003209 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003210 int kwlen;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003211 char_u keyword[MAXKEYWLEN + 1]; // assume max. keyword len is 80
Bram Moolenaardad6b692005-01-25 22:14:34 +00003212 hashtab_T *ht;
3213 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003214
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003215 // Find first character after the keyword. First character was already
3216 // checked.
Bram Moolenaardad6b692005-01-25 22:14:34 +00003217 kwp = line + startcol;
3218 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003219 do
3220 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003221 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003222 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003223 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00003224 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003225 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003226 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003227
Bram Moolenaardad6b692005-01-25 22:14:34 +00003228 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003229 return 0;
3230
3231 /*
3232 * Must make a copy of the keyword, so we can add a NUL and make it
3233 * lowercase.
3234 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003235 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003236
3237 /*
3238 * Try twice:
3239 * 1. matching case
3240 * 2. ignoring case
3241 */
3242 for (round = 1; round <= 2; ++round)
3243 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003244 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003245 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003246 continue;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003247 if (round == 2) // ignore case
Bram Moolenaardad6b692005-01-25 22:14:34 +00003248 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003249
3250 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003251 * Find keywords that match. There can be several with different
3252 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003253 * When current_next_list is non-zero accept only that group, otherwise:
3254 * Accept a not-contained keyword at toplevel.
3255 * Accept a keyword at other levels only if it is in the contains list.
3256 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003257 hi = hash_find(ht, keyword);
3258 if (!HASHITEM_EMPTY(hi))
3259 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003260 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003261 if (current_next_list != 0
3262 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3263 : (cur_si == NULL
3264 ? !(kp->flags & HL_CONTAINED)
3265 : in_id_list(cur_si, cur_si->si_cont_list,
3266 &kp->k_syn, kp->flags & HL_CONTAINED)))
3267 {
3268 *endcolp = startcol + kwlen;
3269 *flagsp = kp->flags;
3270 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003271#ifdef FEAT_CONCEAL
3272 *ccharp = kp->k_char;
3273#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003274 return kp->k_syn.id;
3275 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003276 }
3277 }
3278 return 0;
3279}
3280
3281/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003282 * Handle ":syntax conceal" command.
3283 */
3284 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003285syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003286{
3287#ifdef FEAT_CONCEAL
3288 char_u *arg = eap->arg;
3289 char_u *next;
3290
3291 eap->nextcmd = find_nextcmd(arg);
3292 if (eap->skip)
3293 return;
3294
3295 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003296 if (*arg == NUL)
3297 {
3298 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003299 msg("syntax conceal on");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003300 else
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003301 msg("syntax conceal off");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003302 }
3303 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003304 curwin->w_s->b_syn_conceal = TRUE;
3305 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3306 curwin->w_s->b_syn_conceal = FALSE;
3307 else
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003308 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003309#endif
3310}
3311
3312/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003313 * Handle ":syntax case" command.
3314 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003315 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003316syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003317{
3318 char_u *arg = eap->arg;
3319 char_u *next;
3320
3321 eap->nextcmd = find_nextcmd(arg);
3322 if (eap->skip)
3323 return;
3324
3325 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003326 if (*arg == NUL)
3327 {
3328 if (curwin->w_s->b_syn_ic)
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003329 msg("syntax case ignore");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003330 else
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003331 msg("syntax case match");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003332 }
3333 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003334 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003335 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003336 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003337 else
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003338 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003339}
3340
3341/*
3342 * Handle ":syntax foldlevel" command.
3343 */
3344 static void
3345syn_cmd_foldlevel(exarg_T *eap, int syncing UNUSED)
3346{
3347 char_u *arg = eap->arg;
3348 char_u *arg_end;
3349
3350 eap->nextcmd = find_nextcmd(arg);
3351 if (eap->skip)
3352 return;
3353
3354 if (*arg == NUL)
3355 {
3356 switch (curwin->w_s->b_syn_foldlevel)
3357 {
Dominique Pellecd53eed2022-02-05 18:53:06 +00003358 case SYNFLD_START: msg("syntax foldlevel start"); break;
3359 case SYNFLD_MINIMUM: msg("syntax foldlevel minimum"); break;
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003360 default: break;
3361 }
3362 return;
3363 }
3364
3365 arg_end = skiptowhite(arg);
3366 if (STRNICMP(arg, "start", 5) == 0 && arg_end - arg == 5)
3367 curwin->w_s->b_syn_foldlevel = SYNFLD_START;
3368 else if (STRNICMP(arg, "minimum", 7) == 0 && arg_end - arg == 7)
3369 curwin->w_s->b_syn_foldlevel = SYNFLD_MINIMUM;
3370 else
3371 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003372 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003373 return;
3374 }
3375
3376 arg = skipwhite(arg_end);
3377 if (*arg != NUL)
3378 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003379 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003380 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003381}
3382
3383/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003384 * Handle ":syntax spell" command.
3385 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003386 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003387syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003388{
3389 char_u *arg = eap->arg;
3390 char_u *next;
3391
3392 eap->nextcmd = find_nextcmd(arg);
3393 if (eap->skip)
3394 return;
3395
3396 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003397 if (*arg == NUL)
3398 {
3399 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
Dominique Pellecd53eed2022-02-05 18:53:06 +00003400 msg("syntax spell toplevel");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003401 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
Dominique Pellecd53eed2022-02-05 18:53:06 +00003402 msg("syntax spell notoplevel");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003403 else
Dominique Pellecd53eed2022-02-05 18:53:06 +00003404 msg("syntax spell default");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003405 }
3406 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003407 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003408 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003409 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003410 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003411 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003412 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003413 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003414 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003415 return;
3416 }
3417
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003418 // assume spell checking changed, force a redraw
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003419 redraw_win_later(curwin, UPD_NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003420}
3421
3422/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003423 * Handle ":syntax iskeyword" command.
3424 */
3425 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003426syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003427{
3428 char_u *arg = eap->arg;
3429 char_u save_chartab[32];
3430 char_u *save_isk;
3431
3432 if (eap->skip)
3433 return;
3434
3435 arg = skipwhite(arg);
3436 if (*arg == NUL)
3437 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003438 msg_puts("\n");
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003439 if (curwin->w_s->b_syn_isk != empty_option)
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003440 {
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003441 msg_puts("syntax iskeyword ");
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003442 msg_outtrans(curwin->w_s->b_syn_isk);
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003443 }
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003444 else
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003445 msg_outtrans((char_u *)_("syntax iskeyword not set"));
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003446 }
3447 else
3448 {
3449 if (STRNICMP(arg, "clear", 5) == 0)
3450 {
3451 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3452 (size_t)32);
3453 clear_string_option(&curwin->w_s->b_syn_isk);
3454 }
3455 else
3456 {
3457 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3458 save_isk = curbuf->b_p_isk;
3459 curbuf->b_p_isk = vim_strsave(arg);
3460
3461 buf_init_chartab(curbuf, FALSE);
3462 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3463 (size_t)32);
3464 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3465 clear_string_option(&curwin->w_s->b_syn_isk);
3466 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3467 curbuf->b_p_isk = save_isk;
3468 }
3469 }
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003470 redraw_win_later(curwin, UPD_NOT_VALID);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003471}
3472
3473/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003474 * Clear all syntax info for one buffer.
3475 */
3476 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003477syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003478{
3479 int i;
3480
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003481 block->b_syn_error = FALSE; // clear previous error
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003482#ifdef FEAT_RELTIME
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003483 block->b_syn_slow = FALSE; // clear previous timeout
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003484#endif
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003485 block->b_syn_ic = FALSE; // Use case, by default
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003486 block->b_syn_foldlevel = SYNFLD_START;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003487 block->b_syn_spell = SYNSPL_DEFAULT; // default spell checking
Bram Moolenaar860cae12010-06-05 23:22:07 +02003488 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003489#ifdef FEAT_CONCEAL
3490 block->b_syn_conceal = FALSE;
3491#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003492
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003493 // free the keywords
Bram Moolenaar860cae12010-06-05 23:22:07 +02003494 clear_keywtab(&block->b_keywtab);
3495 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003496
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003497 // free the syntax patterns
Bram Moolenaar860cae12010-06-05 23:22:07 +02003498 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3499 syn_clear_pattern(block, i);
3500 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003501
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003502 // free the syntax clusters
Bram Moolenaar860cae12010-06-05 23:22:07 +02003503 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3504 syn_clear_cluster(block, i);
3505 ga_clear(&block->b_syn_clusters);
3506 block->b_spell_cluster_id = 0;
3507 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003508
Bram Moolenaar860cae12010-06-05 23:22:07 +02003509 block->b_syn_sync_flags = 0;
3510 block->b_syn_sync_minlines = 0;
3511 block->b_syn_sync_maxlines = 0;
3512 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003513
Bram Moolenaar473de612013-06-08 18:19:48 +02003514 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003515 block->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003516 VIM_CLEAR(block->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003517#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003518 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003519#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003520 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003521
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003522 // free the stored states
Bram Moolenaar860cae12010-06-05 23:22:07 +02003523 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003524 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003525
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003526 // Reset the counter for ":syn include"
Bram Moolenaar42431a72011-04-01 14:44:59 +02003527 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003528}
3529
3530/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003531 * Get rid of ownsyntax for window "wp".
3532 */
3533 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003534reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003535{
3536 if (wp->w_s != &wp->w_buffer->b_s)
3537 {
3538 syntax_clear(wp->w_s);
3539 vim_free(wp->w_s);
3540 wp->w_s = &wp->w_buffer->b_s;
3541 }
3542}
3543
3544/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003545 * Clear syncing info for one buffer.
3546 */
3547 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003548syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003549{
3550 int i;
3551
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003552 // free the syntax patterns
Bram Moolenaar860cae12010-06-05 23:22:07 +02003553 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3554 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3555 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003556
Bram Moolenaar860cae12010-06-05 23:22:07 +02003557 curwin->w_s->b_syn_sync_flags = 0;
3558 curwin->w_s->b_syn_sync_minlines = 0;
3559 curwin->w_s->b_syn_sync_maxlines = 0;
3560 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003561
Bram Moolenaar473de612013-06-08 18:19:48 +02003562 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003563 curwin->w_s->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003564 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003565 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003566
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003567 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003568}
3569
3570/*
3571 * Remove one pattern from the buffer's pattern list.
3572 */
3573 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003574syn_remove_pattern(
3575 synblock_T *block,
3576 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003577{
3578 synpat_T *spp;
3579
Bram Moolenaar860cae12010-06-05 23:22:07 +02003580 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003581#ifdef FEAT_FOLDING
3582 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003583 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003584#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003585 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003586 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003587 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3588 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003589}
3590
3591/*
3592 * Clear and free one syntax pattern. When clearing all, must be called from
3593 * last to first!
3594 */
3595 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003596syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003597{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003598 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003599 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003600 // Only free sp_cont_list and sp_next_list of first start pattern
Bram Moolenaar860cae12010-06-05 23:22:07 +02003601 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003602 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003603 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3604 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3605 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003606 }
3607}
3608
3609/*
3610 * Clear and free one syntax cluster.
3611 */
3612 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003613syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003614{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003615 vim_free(SYN_CLSTR(block)[i].scl_name);
3616 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3617 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003618}
3619
3620/*
3621 * Handle ":syntax clear" command.
3622 */
3623 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003624syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003625{
3626 char_u *arg = eap->arg;
3627 char_u *arg_end;
3628 int id;
3629
3630 eap->nextcmd = find_nextcmd(arg);
3631 if (eap->skip)
3632 return;
3633
3634 /*
3635 * We have to disable this within ":syn include @group filename",
3636 * because otherwise @group would get deleted.
3637 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3638 * clear".
3639 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003640 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003641 return;
3642
Bram Moolenaar1966c242020-04-20 22:42:32 +02003643 if (ends_excmd2(eap->cmd, arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003644 {
3645 /*
3646 * No argument: Clear all syntax items.
3647 */
3648 if (syncing)
3649 syntax_sync_clear();
3650 else
3651 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003652 syntax_clear(curwin->w_s);
3653 if (curwin->w_s == &curwin->w_buffer->b_s)
3654 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003655 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003656 }
3657 }
3658 else
3659 {
3660 /*
3661 * Clear the group IDs that are in the argument.
3662 */
Bram Moolenaar1966c242020-04-20 22:42:32 +02003663 while (!ends_excmd2(eap->cmd, arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003664 {
3665 arg_end = skiptowhite(arg);
3666 if (*arg == '@')
3667 {
3668 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3669 if (id == 0)
3670 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003671 semsg(_(e_no_such_syntax_cluster_1), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003672 break;
3673 }
3674 else
3675 {
3676 /*
3677 * We can't physically delete a cluster without changing
3678 * the IDs of other clusters, so we do the next best thing
3679 * and make it empty.
3680 */
3681 short scl_id = id - SYNID_CLUSTER;
3682
Bram Moolenaard23a8232018-02-10 18:45:26 +01003683 VIM_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003684 }
3685 }
3686 else
3687 {
3688 id = syn_namen2id(arg, (int)(arg_end - arg));
3689 if (id == 0)
3690 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +02003691 semsg(_(e_no_such_highlight_group_name_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003692 break;
3693 }
3694 else
3695 syn_clear_one(id, syncing);
3696 }
3697 arg = skipwhite(arg_end);
3698 }
3699 }
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003700 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003701 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003702}
3703
3704/*
3705 * Clear one syntax group for the current buffer.
3706 */
3707 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003708syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003709{
3710 synpat_T *spp;
3711 int idx;
3712
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003713 // Clear keywords only when not ":syn sync clear group-name"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003714 if (!syncing)
3715 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003716 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3717 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003718 }
3719
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003720 // clear the patterns for "id"
Bram Moolenaar860cae12010-06-05 23:22:07 +02003721 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003722 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003723 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003724 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3725 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003726 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003727 }
3728}
3729
3730/*
3731 * Handle ":syntax on" command.
3732 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003733 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003734syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003735{
3736 syn_cmd_onoff(eap, "syntax");
3737}
3738
3739/*
3740 * Handle ":syntax enable" command.
3741 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003742 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003743syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003744{
Bram Moolenaarc8a9fe52021-11-20 19:50:59 +00003745 set_internal_string_var((char_u *)"g:syntax_cmd", (char_u *)"enable");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003746 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003747 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003748}
3749
3750/*
3751 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003752 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003753 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003754 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003755syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003756{
Bram Moolenaar63b91732021-08-05 20:40:03 +02003757 set_nextcmd(eap, eap->arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003758 if (!eap->skip)
3759 {
Bram Moolenaarc8a9fe52021-11-20 19:50:59 +00003760 set_internal_string_var((char_u *)"g:syntax_cmd", (char_u *)"reset");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003761 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003762 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003763 }
3764}
3765
3766/*
3767 * Handle ":syntax manual" command.
3768 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003769 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003770syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003771{
3772 syn_cmd_onoff(eap, "manual");
3773}
3774
3775/*
3776 * Handle ":syntax off" command.
3777 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003778 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003779syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003780{
3781 syn_cmd_onoff(eap, "nosyntax");
3782}
3783
3784 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003785syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003786{
3787 char_u buf[100];
3788
Bram Moolenaar63b91732021-08-05 20:40:03 +02003789 set_nextcmd(eap, eap->arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003790 if (!eap->skip)
3791 {
3792 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003793 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003794 do_cmdline_cmd(buf);
3795 }
3796}
3797
3798/*
3799 * Handle ":syntax [list]" command: list current syntax words.
3800 */
3801 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003802syn_cmd_list(
3803 exarg_T *eap,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003804 int syncing) // when TRUE: list syncing items
Bram Moolenaar071d4272004-06-13 20:20:40 +00003805{
3806 char_u *arg = eap->arg;
3807 int id;
3808 char_u *arg_end;
3809
3810 eap->nextcmd = find_nextcmd(arg);
3811 if (eap->skip)
3812 return;
3813
Bram Moolenaar860cae12010-06-05 23:22:07 +02003814 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003815 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003816 msg(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003817 return;
3818 }
3819
3820 if (syncing)
3821 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003822 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003823 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003824 msg_puts(_("syncing on C-style comments"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003825 syn_lines_msg();
3826 syn_match_msg();
3827 return;
3828 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003829 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003830 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003831 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003832 msg_puts(_("no syncing"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003833 else
3834 {
Bram Moolenaar99502802020-11-18 16:53:23 +01003835 if (curwin->w_s->b_syn_sync_minlines == MAXLNUM)
3836 msg_puts(_("syncing starts at the first line"));
3837 else
3838 {
3839 msg_puts(_("syncing starts "));
3840 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3841 msg_puts(_(" lines before top line"));
3842 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003843 syn_match_msg();
3844 }
3845 return;
3846 }
Bram Moolenaar32526b32019-01-19 17:43:09 +01003847 msg_puts_title(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003848 if (curwin->w_s->b_syn_sync_minlines > 0
3849 || curwin->w_s->b_syn_sync_maxlines > 0
3850 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003851 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003852 msg_puts(_("\nsyncing on items"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003853 syn_lines_msg();
3854 syn_match_msg();
3855 }
3856 }
3857 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01003858 msg_puts_title(_("\n--- Syntax items ---"));
Bram Moolenaar1966c242020-04-20 22:42:32 +02003859 if (ends_excmd2(eap->cmd, arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003860 {
3861 /*
3862 * No argument: List all group IDs and all syntax clusters.
3863 */
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02003864 for (id = 1; id <= highlight_num_groups() && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003865 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003866 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003867 syn_list_cluster(id);
3868 }
3869 else
3870 {
3871 /*
3872 * List the group IDs and syntax clusters that are in the argument.
3873 */
Bram Moolenaar1966c242020-04-20 22:42:32 +02003874 while (!ends_excmd2(eap->cmd, arg) && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003875 {
3876 arg_end = skiptowhite(arg);
3877 if (*arg == '@')
3878 {
3879 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3880 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003881 semsg(_(e_no_such_syntax_cluster_2), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003882 else
3883 syn_list_cluster(id - SYNID_CLUSTER);
3884 }
3885 else
3886 {
3887 id = syn_namen2id(arg, (int)(arg_end - arg));
3888 if (id == 0)
Bram Moolenaare29a27f2021-07-20 21:07:36 +02003889 semsg(_(e_no_such_highlight_group_name_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003890 else
3891 syn_list_one(id, syncing, TRUE);
3892 }
3893 arg = skipwhite(arg_end);
3894 }
3895 }
Bram Moolenaar63b91732021-08-05 20:40:03 +02003896 set_nextcmd(eap, arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003897}
3898
3899 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003900syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003901{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003902 if (curwin->w_s->b_syn_sync_maxlines > 0
3903 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003904 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003905 msg_puts("; ");
Bram Moolenaar99502802020-11-18 16:53:23 +01003906 if (curwin->w_s->b_syn_sync_minlines == MAXLNUM)
3907 msg_puts(_("from the first line"));
3908 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003909 {
Bram Moolenaar99502802020-11-18 16:53:23 +01003910 if (curwin->w_s->b_syn_sync_minlines > 0)
3911 {
3912 msg_puts(_("minimal "));
3913 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3914 if (curwin->w_s->b_syn_sync_maxlines)
3915 msg_puts(", ");
3916 }
3917 if (curwin->w_s->b_syn_sync_maxlines > 0)
3918 {
3919 msg_puts(_("maximal "));
3920 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
3921 }
3922 msg_puts(_(" lines before top line"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003923 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003924 }
3925}
3926
3927 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003928syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003929{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003930 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003931 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003932 msg_puts(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003933 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar32526b32019-01-19 17:43:09 +01003934 msg_puts(_(" line breaks"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003935 }
3936}
3937
3938static int last_matchgroup;
3939
3940struct name_list
3941{
3942 int flag;
3943 char *name;
3944};
3945
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01003946static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003947
3948/*
3949 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3950 */
3951 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003952syn_list_one(
3953 int id,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003954 int syncing, // when TRUE: list syncing items
3955 int link_only) // when TRUE; list link-only too
Bram Moolenaar071d4272004-06-13 20:20:40 +00003956{
3957 int attr;
3958 int idx;
3959 int did_header = FALSE;
3960 synpat_T *spp;
3961 static struct name_list namelist1[] =
3962 {
3963 {HL_DISPLAY, "display"},
3964 {HL_CONTAINED, "contained"},
3965 {HL_ONELINE, "oneline"},
3966 {HL_KEEPEND, "keepend"},
3967 {HL_EXTEND, "extend"},
3968 {HL_EXCLUDENL, "excludenl"},
3969 {HL_TRANSP, "transparent"},
3970 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003971#ifdef FEAT_CONCEAL
3972 {HL_CONCEAL, "conceal"},
3973 {HL_CONCEALENDS, "concealends"},
3974#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003975 {0, NULL}
3976 };
3977 static struct name_list namelist2[] =
3978 {
3979 {HL_SKIPWHITE, "skipwhite"},
3980 {HL_SKIPNL, "skipnl"},
3981 {HL_SKIPEMPTY, "skipempty"},
3982 {0, NULL}
3983 };
3984
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003985 attr = HL_ATTR(HLF_D); // highlight like directories
Bram Moolenaar071d4272004-06-13 20:20:40 +00003986
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003987 // list the keywords for "id"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003988 if (!syncing)
3989 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003990 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3991 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003992 did_header, attr);
3993 }
3994
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003995 // list the patterns for "id"
Bram Moolenaar860cae12010-06-05 23:22:07 +02003996 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003997 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003998 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003999 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4000 continue;
4001
4002 (void)syn_list_header(did_header, 999, id);
4003 did_header = TRUE;
4004 last_matchgroup = 0;
4005 if (spp->sp_type == SPTYPE_MATCH)
4006 {
4007 put_pattern("match", ' ', spp, attr);
4008 msg_putchar(' ');
4009 }
4010 else if (spp->sp_type == SPTYPE_START)
4011 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004012 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4013 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4014 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4015 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4016 while (idx < curwin->w_s->b_syn_patterns.ga_len
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004017 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004018 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004019 --idx;
4020 msg_putchar(' ');
4021 }
4022 syn_list_flags(namelist1, spp->sp_flags, attr);
4023
4024 if (spp->sp_cont_list != NULL)
4025 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4026
4027 if (spp->sp_syn.cont_in_list != NULL)
4028 put_id_list((char_u *)"containedin",
4029 spp->sp_syn.cont_in_list, attr);
4030
4031 if (spp->sp_next_list != NULL)
4032 {
4033 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4034 syn_list_flags(namelist2, spp->sp_flags, attr);
4035 }
4036 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4037 {
4038 if (spp->sp_flags & HL_SYNC_HERE)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004039 msg_puts_attr("grouphere", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004040 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004041 msg_puts_attr("groupthere", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004042 msg_putchar(' ');
4043 if (spp->sp_sync_idx >= 0)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004044 msg_outtrans(highlight_group_name(SYN_ITEMS(curwin->w_s)
4045 [spp->sp_sync_idx].sp_syn.id - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004046 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004047 msg_puts("NONE");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004048 msg_putchar(' ');
4049 }
4050 }
4051
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004052 // list the link, if there is one
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004053 if (highlight_link_id(id - 1) && (did_header || link_only) && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004054 {
4055 (void)syn_list_header(did_header, 999, id);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004056 msg_puts_attr("links to", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004057 msg_putchar(' ');
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004058 msg_outtrans(highlight_group_name(highlight_link_id(id - 1) - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004059 }
4060}
4061
4062 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004063syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004064{
4065 int i;
4066
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004067 for (i = 0; nlist[i].flag != 0; ++i)
4068 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004069 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004070 msg_puts_attr(nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004071 msg_putchar(' ');
4072 }
4073}
4074
4075/*
4076 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4077 */
4078 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004079syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004080{
4081 int endcol = 15;
4082
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004083 // slight hack: roughly duplicate the guts of syn_list_header()
Bram Moolenaar071d4272004-06-13 20:20:40 +00004084 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004085 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004086
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004087 if (msg_col >= endcol) // output at least one space
Bram Moolenaar071d4272004-06-13 20:20:40 +00004088 endcol = msg_col + 1;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004089 if (Columns <= endcol) // avoid hang for tiny window
Bram Moolenaar071d4272004-06-13 20:20:40 +00004090 endcol = Columns - 1;
4091
4092 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004093 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004094 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004095 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004096 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004097 }
4098 else
4099 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004100 msg_puts_attr("cluster", HL_ATTR(HLF_D));
4101 msg_puts("=NONE");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004102 }
4103}
4104
4105 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004106put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004107{
4108 short *p;
4109
Bram Moolenaar32526b32019-01-19 17:43:09 +01004110 msg_puts_attr((char *)name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004111 msg_putchar('=');
4112 for (p = list; *p; ++p)
4113 {
4114 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4115 {
4116 if (p[1])
Bram Moolenaar32526b32019-01-19 17:43:09 +01004117 msg_puts("ALLBUT");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004118 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004119 msg_puts("ALL");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004120 }
4121 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4122 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004123 msg_puts("TOP");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004124 }
4125 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4126 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004127 msg_puts("CONTAINED");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004128 }
4129 else if (*p >= SYNID_CLUSTER)
4130 {
4131 short scl_id = *p - SYNID_CLUSTER;
4132
4133 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004134 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004135 }
4136 else
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004137 msg_outtrans(highlight_group_name(*p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004138 if (p[1])
4139 msg_putchar(',');
4140 }
4141 msg_putchar(' ');
4142}
4143
4144 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004145put_pattern(
4146 char *s,
4147 int c,
4148 synpat_T *spp,
4149 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004150{
4151 long n;
4152 int mask;
4153 int first;
4154 static char *sepchars = "/+=-#@\"|'^&";
4155 int i;
4156
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004157 // May have to write "matchgroup=group"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004158 if (last_matchgroup != spp->sp_syn_match_id)
4159 {
4160 last_matchgroup = spp->sp_syn_match_id;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004161 msg_puts_attr("matchgroup", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004162 msg_putchar('=');
4163 if (last_matchgroup == 0)
4164 msg_outtrans((char_u *)"NONE");
4165 else
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004166 msg_outtrans(highlight_group_name(last_matchgroup - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004167 msg_putchar(' ');
4168 }
4169
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004170 // output the name of the pattern and an '=' or ' '
Bram Moolenaar32526b32019-01-19 17:43:09 +01004171 msg_puts_attr(s, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004172 msg_putchar(c);
4173
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004174 // output the pattern, in between a char that is not in the pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00004175 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4176 if (sepchars[++i] == NUL)
4177 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004178 i = 0; // no good char found, just use the first one
Bram Moolenaar071d4272004-06-13 20:20:40 +00004179 break;
4180 }
4181 msg_putchar(sepchars[i]);
4182 msg_outtrans(spp->sp_pattern);
4183 msg_putchar(sepchars[i]);
4184
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004185 // output any pattern options
Bram Moolenaar071d4272004-06-13 20:20:40 +00004186 first = TRUE;
4187 for (i = 0; i < SPO_COUNT; ++i)
4188 {
4189 mask = (1 << i);
4190 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4191 {
4192 if (!first)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004193 msg_putchar(','); // separate with commas
Bram Moolenaar32526b32019-01-19 17:43:09 +01004194 msg_puts(spo_name_tab[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004195 n = spp->sp_offsets[i];
4196 if (i != SPO_LC_OFF)
4197 {
4198 if (spp->sp_off_flags & mask)
4199 msg_putchar('s');
4200 else
4201 msg_putchar('e');
4202 if (n > 0)
4203 msg_putchar('+');
4204 }
4205 if (n || i == SPO_LC_OFF)
4206 msg_outnum(n);
4207 first = FALSE;
4208 }
4209 }
4210 msg_putchar(' ');
4211}
4212
4213/*
4214 * List or clear the keywords for one syntax group.
4215 * Return TRUE if the header has been printed.
4216 */
4217 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004218syn_list_keywords(
4219 int id,
4220 hashtab_T *ht,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004221 int did_header, // header has already been printed
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004222 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004223{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004224 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004225 hashitem_T *hi;
4226 keyentry_T *kp;
4227 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004228 int prev_contained = 0;
4229 short *prev_next_list = NULL;
4230 short *prev_cont_in_list = NULL;
4231 int prev_skipnl = 0;
4232 int prev_skipwhite = 0;
4233 int prev_skipempty = 0;
4234
Bram Moolenaar071d4272004-06-13 20:20:40 +00004235 /*
4236 * Unfortunately, this list of keywords is not sorted on alphabet but on
4237 * hash value...
4238 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004239 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004240 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004241 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004242 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004243 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004244 --todo;
4245 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004246 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004247 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004248 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004249 if (prev_contained != (kp->flags & HL_CONTAINED)
4250 || prev_skipnl != (kp->flags & HL_SKIPNL)
4251 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4252 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4253 || prev_cont_in_list != kp->k_syn.cont_in_list
4254 || prev_next_list != kp->next_list)
4255 outlen = 9999;
4256 else
4257 outlen = (int)STRLEN(kp->keyword);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004258 // output "contained" and "nextgroup" on each line
Bram Moolenaardad6b692005-01-25 22:14:34 +00004259 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004260 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004261 prev_contained = 0;
4262 prev_next_list = NULL;
4263 prev_cont_in_list = NULL;
4264 prev_skipnl = 0;
4265 prev_skipwhite = 0;
4266 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004267 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004268 did_header = TRUE;
4269 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004270 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004271 msg_puts_attr("contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004272 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004273 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004274 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004275 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004276 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004277 put_id_list((char_u *)"containedin",
4278 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004279 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004280 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004281 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004282 if (kp->next_list != prev_next_list)
4283 {
4284 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4285 msg_putchar(' ');
4286 prev_next_list = kp->next_list;
4287 if (kp->flags & HL_SKIPNL)
4288 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004289 msg_puts_attr("skipnl", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004290 msg_putchar(' ');
4291 prev_skipnl = (kp->flags & HL_SKIPNL);
4292 }
4293 if (kp->flags & HL_SKIPWHITE)
4294 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004295 msg_puts_attr("skipwhite", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004296 msg_putchar(' ');
4297 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4298 }
4299 if (kp->flags & HL_SKIPEMPTY)
4300 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004301 msg_puts_attr("skipempty", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004302 msg_putchar(' ');
4303 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4304 }
4305 }
4306 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004307 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004308 }
4309 }
4310 }
4311
4312 return did_header;
4313}
4314
4315 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004316syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004317{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004318 hashitem_T *hi;
4319 keyentry_T *kp;
4320 keyentry_T *kp_prev;
4321 keyentry_T *kp_next;
4322 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004323
Bram Moolenaardad6b692005-01-25 22:14:34 +00004324 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004325 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004326 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004327 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004328 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004329 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004330 --todo;
4331 kp_prev = NULL;
4332 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004333 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004334 if (kp->k_syn.id == id)
4335 {
4336 kp_next = kp->ke_next;
4337 if (kp_prev == NULL)
4338 {
4339 if (kp_next == NULL)
4340 hash_remove(ht, hi);
4341 else
4342 hi->hi_key = KE2HIKEY(kp_next);
4343 }
4344 else
4345 kp_prev->ke_next = kp_next;
4346 vim_free(kp->next_list);
4347 vim_free(kp->k_syn.cont_in_list);
4348 vim_free(kp);
4349 kp = kp_next;
4350 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004351 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004352 {
4353 kp_prev = kp;
4354 kp = kp->ke_next;
4355 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004356 }
4357 }
4358 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004359 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004360}
4361
4362/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004363 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004364 */
4365 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004366clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004367{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004368 hashitem_T *hi;
4369 int todo;
4370 keyentry_T *kp;
4371 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004372
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004373 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004374 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004375 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004376 if (!HASHITEM_EMPTY(hi))
4377 {
4378 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004379 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004380 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004381 kp_next = kp->ke_next;
4382 vim_free(kp->next_list);
4383 vim_free(kp->k_syn.cont_in_list);
4384 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004385 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004386 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004387 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004388 hash_clear(ht);
4389 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004390}
4391
4392/*
4393 * Add a keyword to the list of keywords.
4394 */
4395 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004396add_keyword(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004397 char_u *name, // name of keyword
4398 int id, // group ID for this keyword
4399 int flags, // flags for this keyword
4400 short *cont_in_list, // containedin for this keyword
4401 short *next_list, // nextgroup for this keyword
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004402 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004403{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004404 keyentry_T *kp;
4405 hashtab_T *ht;
4406 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004407 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004408 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004409 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004410
Bram Moolenaar860cae12010-06-05 23:22:07 +02004411 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004412 name_ic = str_foldcase(name, (int)STRLEN(name),
4413 name_folded, MAXKEYWLEN + 1);
4414 else
4415 name_ic = name;
Bram Moolenaar47ed5532019-08-08 20:49:14 +02004416 kp = alloc(offsetof(keyentry_T, keyword) + STRLEN(name_ic) + 1);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004417 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004418 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004419 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004420 kp->k_syn.id = id;
4421 kp->k_syn.inc_tag = current_syn_inc_tag;
4422 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004423 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004424 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004425 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004426 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004427 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004428
Bram Moolenaar860cae12010-06-05 23:22:07 +02004429 if (curwin->w_s->b_syn_ic)
4430 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004431 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004432 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004433
Bram Moolenaardad6b692005-01-25 22:14:34 +00004434 hash = hash_hash(kp->keyword);
4435 hi = hash_lookup(ht, kp->keyword, hash);
4436 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004437 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004438 // new keyword, add to hashtable
Bram Moolenaardad6b692005-01-25 22:14:34 +00004439 kp->ke_next = NULL;
4440 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004442 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004443 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004444 // keyword already exists, prepend to list
Bram Moolenaardad6b692005-01-25 22:14:34 +00004445 kp->ke_next = HI2KE(hi);
4446 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004447 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004448}
4449
4450/*
4451 * Get the start and end of the group name argument.
4452 * Return a pointer to the first argument.
4453 * Return NULL if the end of the command was found instead of further args.
4454 */
4455 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004456get_group_name(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004457 char_u *arg, // start of the argument
4458 char_u **name_end) // pointer to end of the name
Bram Moolenaar071d4272004-06-13 20:20:40 +00004459{
4460 char_u *rest;
4461
4462 *name_end = skiptowhite(arg);
4463 rest = skipwhite(*name_end);
4464
4465 /*
4466 * Check if there are enough arguments. The first argument may be a
4467 * pattern, where '|' is allowed, so only check for NUL.
4468 */
4469 if (ends_excmd(*arg) || *rest == NUL)
4470 return NULL;
4471 return rest;
4472}
4473
4474/*
4475 * Check for syntax command option arguments.
4476 * This can be called at any place in the list of arguments, and just picks
4477 * out the arguments that are known. Can be called several times in a row to
4478 * collect all options in between other arguments.
4479 * Return a pointer to the next argument (which isn't an option).
4480 * Return NULL for any error;
4481 */
4482 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004483get_syn_options(
Bram Moolenaar1966c242020-04-20 22:42:32 +02004484 char_u *start, // next argument to be checked
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004485 syn_opt_arg_T *opt, // various things
Bram Moolenaarde318c52017-01-17 16:27:10 +01004486 int *conceal_char UNUSED,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004487 int skip) // TRUE if skipping over command
Bram Moolenaar071d4272004-06-13 20:20:40 +00004488{
Bram Moolenaar1966c242020-04-20 22:42:32 +02004489 char_u *arg = start;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004490 char_u *gname_start, *gname;
4491 int syn_id;
4492 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004493 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004494 int i;
4495 int fidx;
4496 static struct flag
4497 {
4498 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004499 int argtype;
4500 int flags;
4501 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4502 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4503 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4504 {"eExXtTeEnNdD", 0, HL_EXTEND},
4505 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4506 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4507 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4508 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4509 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4510 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4511 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4512 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4513 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004514 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4515 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4516 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004517 {"cCoOnNtTaAiInNsS", 1, 0},
4518 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4519 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004520 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004521 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004522
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004523 if (arg == NULL) // already detected error
Bram Moolenaar071d4272004-06-13 20:20:40 +00004524 return NULL;
4525
Bram Moolenaar860cae12010-06-05 23:22:07 +02004526#ifdef FEAT_CONCEAL
4527 if (curwin->w_s->b_syn_conceal)
4528 opt->flags |= HL_CONCEAL;
4529#endif
4530
Bram Moolenaar071d4272004-06-13 20:20:40 +00004531 for (;;)
4532 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004533 /*
4534 * This is used very often when a large number of keywords is defined.
4535 * Need to skip quickly when no option name is found.
4536 * Also avoid tolower(), it's slow.
4537 */
4538 if (strchr(first_letters, *arg) == NULL)
4539 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004540
K.Takataeeec2542021-06-02 13:28:16 +02004541 for (fidx = ARRAY_LENGTH(flagtab); --fidx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004542 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004543 p = flagtab[fidx].name;
4544 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4545 if (arg[len] != p[i] && arg[len] != p[i + 1])
4546 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004547 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004548 || (flagtab[fidx].argtype > 0
4549 ? arg[len] == '='
Bram Moolenaar1966c242020-04-20 22:42:32 +02004550 : ends_excmd2(start, arg + len))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004551 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004552 if (opt->keyword
4553 && (flagtab[fidx].flags == HL_DISPLAY
4554 || flagtab[fidx].flags == HL_FOLD
4555 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004556 // treat "display", "fold" and "extend" as a keyword
Bram Moolenaar071d4272004-06-13 20:20:40 +00004557 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004558 break;
4559 }
4560 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004561 if (fidx < 0) // no match found
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004562 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004563
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004564 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004565 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004566 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004567 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004568 emsg(_(e_contains_argument_not_accepted_here));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004569 return NULL;
4570 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004571 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004572 return NULL;
4573 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004574 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004575 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004576 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004577 return NULL;
4578 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004579 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004580 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004581 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004582 return NULL;
4583 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004584 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4585 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004586 // cchar=?
Bram Moolenaar860cae12010-06-05 23:22:07 +02004587 if (has_mbyte)
4588 {
Bram Moolenaar264b74f2019-01-24 17:18:42 +01004589#ifdef FEAT_CONCEAL
Bram Moolenaar860cae12010-06-05 23:22:07 +02004590 *conceal_char = mb_ptr2char(arg + 6);
Bram Moolenaar264b74f2019-01-24 17:18:42 +01004591#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004592 arg += mb_ptr2len(arg + 6) - 1;
4593 }
4594 else
Bram Moolenaar56be9502010-06-06 14:20:26 +02004595 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004596#ifdef FEAT_CONCEAL
4597 *conceal_char = arg[6];
4598#else
4599 ;
4600#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004601 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004602#ifdef FEAT_CONCEAL
4603 if (!vim_isprintc_strict(*conceal_char))
4604 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00004605 emsg(_(e_invalid_cchar_value));
Bram Moolenaar4124e722011-01-22 00:58:20 +01004606 return NULL;
4607 }
4608#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004609 arg = skipwhite(arg + 7);
4610 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004611 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004612 {
4613 opt->flags |= flagtab[fidx].flags;
4614 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004615
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004616 if (flagtab[fidx].flags == HL_SYNC_HERE
4617 || flagtab[fidx].flags == HL_SYNC_THERE)
4618 {
4619 if (opt->sync_idx == NULL)
4620 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004621 emsg(_(e_groupthere_not_accepted_here));
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004622 return NULL;
4623 }
4624 gname_start = arg;
4625 arg = skiptowhite(arg);
4626 if (gname_start == arg)
4627 return NULL;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02004628 gname = vim_strnsave(gname_start, arg - gname_start);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004629 if (gname == NULL)
4630 return NULL;
4631 if (STRCMP(gname, "NONE") == 0)
4632 *opt->sync_idx = NONE_IDX;
4633 else
4634 {
4635 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004636 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4637 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
Bram Moolenaar71ccd032020-06-12 22:59:11 +02004638 && SYN_ITEMS(curwin->w_s)[i].sp_type
4639 == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004640 {
4641 *opt->sync_idx = i;
4642 break;
4643 }
4644 if (i < 0)
4645 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004646 semsg(_(e_didnt_find_region_item_for_str), gname);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004647 vim_free(gname);
4648 return NULL;
4649 }
4650 }
4651
4652 vim_free(gname);
4653 arg = skipwhite(arg);
4654 }
4655#ifdef FEAT_FOLDING
4656 else if (flagtab[fidx].flags == HL_FOLD
4657 && foldmethodIsSyntax(curwin))
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004658 // Need to update folds later.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004659 foldUpdateAll(curwin);
4660#endif
4661 }
4662 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004663
4664 return arg;
4665}
4666
4667/*
4668 * Adjustments to syntax item when declared in a ":syn include"'d file.
4669 * Set the contained flag, and if the item is not already contained, add it
4670 * to the specified top-level group, if any.
4671 */
4672 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004673syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004674{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004675 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004676 return;
4677 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004678 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004679 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004680 // We have to alloc this, because syn_combine_list() will free it.
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004681 short *grp_list = ALLOC_MULT(short, 2);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004682 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004683
4684 if (grp_list != NULL)
4685 {
4686 grp_list[0] = id;
4687 grp_list[1] = 0;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004688 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list,
4689 &grp_list, CLUSTER_ADD);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004690 }
4691 }
4692}
4693
4694/*
4695 * Handle ":syntax include [@{group-name}] filename" command.
4696 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004697 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004698syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004699{
4700 char_u *arg = eap->arg;
4701 int sgl_id = 1;
4702 char_u *group_name_end;
4703 char_u *rest;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004704 char *errormsg = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004705 int prev_toplvl_grp;
4706 int prev_syn_inc_tag;
4707 int source = FALSE;
4708
4709 eap->nextcmd = find_nextcmd(arg);
4710 if (eap->skip)
4711 return;
4712
4713 if (arg[0] == '@')
4714 {
4715 ++arg;
4716 rest = get_group_name(arg, &group_name_end);
4717 if (rest == NULL)
4718 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004719 emsg(_(e_filename_required));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004720 return;
4721 }
4722 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004723 if (sgl_id == 0)
4724 return;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004725 // separate_nextcmd() and expand_filename() depend on this
Bram Moolenaar071d4272004-06-13 20:20:40 +00004726 eap->arg = rest;
4727 }
4728
4729 /*
4730 * Everything that's left, up to the next command, should be the
4731 * filename to include.
4732 */
Bram Moolenaar8071cb22019-07-12 17:58:01 +02004733 eap->argt |= (EX_XFILE | EX_NOSPC);
Bram Moolenaarac485062022-03-23 19:45:01 +00004734 separate_nextcmd(eap, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004735 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4736 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004737 // For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4738 // file. Need to expand the file name first. In other cases
4739 // ":runtime!" is used.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004740 source = TRUE;
4741 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4742 {
4743 if (errormsg != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004744 emsg(errormsg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004745 return;
4746 }
4747 }
4748
4749 /*
4750 * Save and restore the existing top-level grouplist id and ":syn
4751 * include" tag around the actual inclusion.
4752 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004753 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4754 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00004755 emsg(_(e_too_many_syntax_includes));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004756 return;
4757 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004758 prev_syn_inc_tag = current_syn_inc_tag;
4759 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004760 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4761 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01004762 if (source ? do_source(eap->arg, FALSE, DOSO_NONE, NULL) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004763 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004764 semsg(_(e_cant_open_file_str), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004765 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004766 current_syn_inc_tag = prev_syn_inc_tag;
4767}
4768
4769/*
4770 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4771 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004772 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004773syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004774{
4775 char_u *arg = eap->arg;
4776 char_u *group_name_end;
4777 int syn_id;
4778 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004779 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004780 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004781 char_u *kw;
4782 syn_opt_arg_T syn_opt_arg;
4783 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004784 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004785
4786 rest = get_group_name(arg, &group_name_end);
4787
4788 if (rest != NULL)
4789 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004790 if (eap->skip)
4791 syn_id = -1;
4792 else
4793 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004794 if (syn_id != 0)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004795 // allocate a buffer, for removing backslashes in the keyword
Bram Moolenaar964b3742019-05-24 18:54:09 +02004796 keyword_copy = alloc(STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004797 if (keyword_copy != NULL)
4798 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004799 syn_opt_arg.flags = 0;
4800 syn_opt_arg.keyword = TRUE;
4801 syn_opt_arg.sync_idx = NULL;
4802 syn_opt_arg.has_cont_list = FALSE;
4803 syn_opt_arg.cont_in_list = NULL;
4804 syn_opt_arg.next_list = NULL;
4805
Bram Moolenaar071d4272004-06-13 20:20:40 +00004806 /*
4807 * The options given apply to ALL keywords, so all options must be
4808 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004809 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004810 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004811 cnt = 0;
4812 p = keyword_copy;
Bram Moolenaar1966c242020-04-20 22:42:32 +02004813 for ( ; rest != NULL && !ends_excmd2(eap->arg, rest);
4814 rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004815 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004816 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4817 eap->skip);
Bram Moolenaar1966c242020-04-20 22:42:32 +02004818 if (rest == NULL || ends_excmd2(eap->arg, rest))
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004819 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004820 // Copy the keyword, removing backslashes, and add a NUL.
Bram Moolenaar1c465442017-03-12 20:10:05 +01004821 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004822 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004823 if (*rest == '\\' && rest[1] != NUL)
4824 ++rest;
4825 *p++ = *rest++;
4826 }
4827 *p++ = NUL;
4828 ++cnt;
4829 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004830
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004831 if (!eap->skip)
4832 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004833 // Adjust flags for use of ":syn include".
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004834 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4835
4836 /*
4837 * 2: Add an entry for each keyword.
4838 */
4839 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4840 {
4841 for (p = vim_strchr(kw, '['); ; )
4842 {
4843 if (p != NULL)
4844 *p = NUL;
4845 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004846 syn_opt_arg.cont_in_list,
4847 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004848 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004849 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004850 if (p[1] == NUL)
4851 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00004852 semsg(_(e_error_missing_rsb_str), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004853 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004854 }
4855 if (p[1] == ']')
4856 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004857 if (p[2] != NUL)
4858 {
Bram Moolenaard82a47d2022-01-05 20:24:39 +00004859 semsg(_(e_trailing_char_after_rsb_str_str),
4860 kw, &p[2]);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004861 goto error;
4862 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004863 kw = p + 1; // skip over the "]"
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004864 break;
4865 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004866 if (has_mbyte)
4867 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004868 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004869
4870 mch_memmove(p, p + 1, l);
4871 p += l;
4872 }
4873 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004874 {
4875 p[0] = p[1];
4876 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004877 }
4878 }
4879 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004880 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004881error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004882 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004883 vim_free(syn_opt_arg.cont_in_list);
4884 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004885 }
4886 }
4887
4888 if (rest != NULL)
Bram Moolenaar63b91732021-08-05 20:40:03 +02004889 set_nextcmd(eap, rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004890 else
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00004891 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004892
Bram Moolenaara4d158b2022-08-14 14:17:45 +01004893 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004894 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004895}
4896
4897/*
4898 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4899 *
4900 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4901 */
4902 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004903syn_cmd_match(
4904 exarg_T *eap,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004905 int syncing) // TRUE for ":syntax sync match .. "
Bram Moolenaar071d4272004-06-13 20:20:40 +00004906{
4907 char_u *arg = eap->arg;
4908 char_u *group_name_end;
4909 char_u *rest;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004910 synpat_T item; // the item found in the line
Bram Moolenaar071d4272004-06-13 20:20:40 +00004911 int syn_id;
4912 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004913 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004914 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004915 int conceal_char = NUL;
Bram Moolenaar1966c242020-04-20 22:42:32 +02004916 int orig_called_emsg = called_emsg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004917
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004918 // Isolate the group name, check for validity
Bram Moolenaar071d4272004-06-13 20:20:40 +00004919 rest = get_group_name(arg, &group_name_end);
4920
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004921 // Get options before the pattern
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004922 syn_opt_arg.flags = 0;
4923 syn_opt_arg.keyword = FALSE;
4924 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4925 syn_opt_arg.has_cont_list = TRUE;
4926 syn_opt_arg.cont_list = NULL;
4927 syn_opt_arg.cont_in_list = NULL;
4928 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01004929 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004930
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004931 // get the pattern.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004932 init_syn_patterns();
Bram Moolenaara80faa82020-04-12 19:37:17 +02004933 CLEAR_FIELD(item);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004934 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004935 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4936 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004937
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004938 // Get options after the pattern
Bram Moolenaarde318c52017-01-17 16:27:10 +01004939 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004940
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004941 if (rest != NULL) // all arguments are valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00004942 {
4943 /*
4944 * Check for trailing command and illegal trailing arguments.
4945 */
Bram Moolenaar63b91732021-08-05 20:40:03 +02004946 set_nextcmd(eap, rest);
Bram Moolenaar1966c242020-04-20 22:42:32 +02004947 if (!ends_excmd2(eap->cmd, rest) || eap->skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004948 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004949 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004950 && (syn_id = syn_check_group(arg,
4951 (int)(group_name_end - arg))) != 0)
4952 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004953 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004954 /*
4955 * Store the pattern in the syn_items list
4956 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004957 idx = curwin->w_s->b_syn_patterns.ga_len;
4958 SYN_ITEMS(curwin->w_s)[idx] = item;
4959 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4960 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4961 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4962 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4963 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4964 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4965 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4966 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004967 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004968#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02004969 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004970#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004971 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004972 curwin->w_s->b_syn_containedin = TRUE;
4973 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4974 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004975
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004976 // remember that we found a match for syncing on
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004977 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004978 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004979#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004980 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004981 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004982#endif
4983
Bram Moolenaara4d158b2022-08-14 14:17:45 +01004984 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004985 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
4986 return; // don't free the progs and patterns now
Bram Moolenaar071d4272004-06-13 20:20:40 +00004987 }
4988 }
4989
4990 /*
4991 * Something failed, free the allocated memory.
4992 */
Bram Moolenaar473de612013-06-08 18:19:48 +02004993 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004994 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004995 vim_free(syn_opt_arg.cont_list);
4996 vim_free(syn_opt_arg.cont_in_list);
4997 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004998
Bram Moolenaar1966c242020-04-20 22:42:32 +02004999 if (rest == NULL && called_emsg == orig_called_emsg)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005000 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005001}
5002
5003/*
5004 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5005 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5006 */
5007 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005008syn_cmd_region(
5009 exarg_T *eap,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005010 int syncing) // TRUE for ":syntax sync region .."
Bram Moolenaar071d4272004-06-13 20:20:40 +00005011{
5012 char_u *arg = eap->arg;
5013 char_u *group_name_end;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005014 char_u *rest; // next arg, NULL on error
Bram Moolenaar071d4272004-06-13 20:20:40 +00005015 char_u *key_end;
5016 char_u *key = NULL;
5017 char_u *p;
5018 int item;
5019#define ITEM_START 0
5020#define ITEM_SKIP 1
5021#define ITEM_END 2
5022#define ITEM_MATCHGROUP 3
5023 struct pat_ptr
5024 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005025 synpat_T *pp_synp; // pointer to syn_pattern
5026 int pp_matchgroup_id; // matchgroup ID
5027 struct pat_ptr *pp_next; // pointer to next pat_ptr
Bram Moolenaar071d4272004-06-13 20:20:40 +00005028 } *(pat_ptrs[3]);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005029 // patterns found in the line
Bram Moolenaar071d4272004-06-13 20:20:40 +00005030 struct pat_ptr *ppp;
5031 struct pat_ptr *ppp_next;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005032 int pat_count = 0; // nr of syn_patterns found
Bram Moolenaar071d4272004-06-13 20:20:40 +00005033 int syn_id;
5034 int matchgroup_id = 0;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005035 int not_enough = FALSE; // not enough arguments
5036 int illegal = FALSE; // illegal arguments
Bram Moolenaar071d4272004-06-13 20:20:40 +00005037 int success = FALSE;
5038 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005039 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005040 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005041
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005042 // Isolate the group name, check for validity
Bram Moolenaar071d4272004-06-13 20:20:40 +00005043 rest = get_group_name(arg, &group_name_end);
5044
5045 pat_ptrs[0] = NULL;
5046 pat_ptrs[1] = NULL;
5047 pat_ptrs[2] = NULL;
5048
5049 init_syn_patterns();
5050
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005051 syn_opt_arg.flags = 0;
5052 syn_opt_arg.keyword = FALSE;
5053 syn_opt_arg.sync_idx = NULL;
5054 syn_opt_arg.has_cont_list = TRUE;
5055 syn_opt_arg.cont_list = NULL;
5056 syn_opt_arg.cont_in_list = NULL;
5057 syn_opt_arg.next_list = NULL;
5058
Bram Moolenaar071d4272004-06-13 20:20:40 +00005059 /*
5060 * get the options, patterns and matchgroup.
5061 */
Bram Moolenaar1966c242020-04-20 22:42:32 +02005062 while (rest != NULL && !ends_excmd2(eap->cmd, rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005063 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005064 // Check for option arguments
Bram Moolenaarde318c52017-01-17 16:27:10 +01005065 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar1966c242020-04-20 22:42:32 +02005066 if (rest == NULL || ends_excmd2(eap->cmd, rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005067 break;
5068
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005069 // must be a pattern or matchgroup then
Bram Moolenaar071d4272004-06-13 20:20:40 +00005070 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005071 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005072 ++key_end;
5073 vim_free(key);
Bram Moolenaardf44a272020-06-07 20:49:05 +02005074 key = vim_strnsave_up(rest, key_end - rest);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005075 if (key == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00005076 {
5077 rest = NULL;
5078 break;
5079 }
5080 if (STRCMP(key, "MATCHGROUP") == 0)
5081 item = ITEM_MATCHGROUP;
5082 else if (STRCMP(key, "START") == 0)
5083 item = ITEM_START;
5084 else if (STRCMP(key, "END") == 0)
5085 item = ITEM_END;
5086 else if (STRCMP(key, "SKIP") == 0)
5087 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005088 if (pat_ptrs[ITEM_SKIP] != NULL) // one skip pattern allowed
Bram Moolenaar071d4272004-06-13 20:20:40 +00005089 {
5090 illegal = TRUE;
5091 break;
5092 }
5093 item = ITEM_SKIP;
5094 }
5095 else
5096 break;
5097 rest = skipwhite(key_end);
5098 if (*rest != '=')
5099 {
5100 rest = NULL;
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005101 semsg(_(e_missing_equal_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005102 break;
5103 }
5104 rest = skipwhite(rest + 1);
5105 if (*rest == NUL)
5106 {
5107 not_enough = TRUE;
5108 break;
5109 }
5110
5111 if (item == ITEM_MATCHGROUP)
5112 {
5113 p = skiptowhite(rest);
5114 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5115 matchgroup_id = 0;
5116 else
5117 {
5118 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5119 if (matchgroup_id == 0)
5120 {
5121 illegal = TRUE;
5122 break;
5123 }
5124 }
5125 rest = skipwhite(p);
5126 }
5127 else
5128 {
5129 /*
5130 * Allocate room for a syn_pattern, and link it in the list of
5131 * syn_patterns for this item, at the start (because the list is
5132 * used from end to start).
5133 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005134 ppp = ALLOC_ONE(struct pat_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005135 if (ppp == NULL)
5136 {
5137 rest = NULL;
5138 break;
5139 }
5140 ppp->pp_next = pat_ptrs[item];
5141 pat_ptrs[item] = ppp;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005142 ppp->pp_synp = ALLOC_CLEAR_ONE(synpat_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005143 if (ppp->pp_synp == NULL)
5144 {
5145 rest = NULL;
5146 break;
5147 }
5148
5149 /*
5150 * Get the syntax pattern and the following offset(s).
5151 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005152 // Enable the appropriate \z specials.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005153 if (item == ITEM_START)
5154 reg_do_extmatch = REX_SET;
5155 else if (item == ITEM_SKIP || item == ITEM_END)
5156 reg_do_extmatch = REX_USE;
5157 rest = get_syn_pattern(rest, ppp->pp_synp);
5158 reg_do_extmatch = 0;
5159 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005160 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005161 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5162 ppp->pp_matchgroup_id = matchgroup_id;
5163 ++pat_count;
5164 }
5165 }
5166 vim_free(key);
5167 if (illegal || not_enough)
5168 rest = NULL;
5169
5170 /*
5171 * Must have a "start" and "end" pattern.
5172 */
5173 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5174 pat_ptrs[ITEM_END] == NULL))
5175 {
5176 not_enough = TRUE;
5177 rest = NULL;
5178 }
5179
5180 if (rest != NULL)
5181 {
5182 /*
5183 * Check for trailing garbage or command.
5184 * If OK, add the item.
5185 */
Bram Moolenaar63b91732021-08-05 20:40:03 +02005186 set_nextcmd(eap, rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005187 if (!ends_excmd(*rest) || eap->skip)
5188 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005189 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005190 && (syn_id = syn_check_group(arg,
5191 (int)(group_name_end - arg))) != 0)
5192 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005193 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005194 /*
5195 * Store the start/skip/end in the syn_items list
5196 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005197 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005198 for (item = ITEM_START; item <= ITEM_END; ++item)
5199 {
5200 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5201 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005202 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5203 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5204 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005205 (item == ITEM_START) ? SPTYPE_START :
5206 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005207 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5208 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005209 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5210 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005211 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005212 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005213#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005214 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005215#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005216 if (item == ITEM_START)
5217 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005218 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005219 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005220 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005221 syn_opt_arg.cont_in_list;
5222 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005223 curwin->w_s->b_syn_containedin = TRUE;
5224 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005225 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005226 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005227 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005228 ++idx;
5229#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005230 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005231 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005232#endif
5233 }
5234 }
5235
Bram Moolenaara4d158b2022-08-14 14:17:45 +01005236 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005237 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
5238 success = TRUE; // don't free the progs and patterns now
Bram Moolenaar071d4272004-06-13 20:20:40 +00005239 }
5240 }
5241
5242 /*
5243 * Free the allocated memory.
5244 */
5245 for (item = ITEM_START; item <= ITEM_END; ++item)
5246 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5247 {
Bram Moolenaar4bbfb0f2019-08-31 15:28:02 +02005248 if (!success && ppp->pp_synp != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005249 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005250 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005251 vim_free(ppp->pp_synp->sp_pattern);
5252 }
5253 vim_free(ppp->pp_synp);
5254 ppp_next = ppp->pp_next;
5255 vim_free(ppp);
5256 }
5257
5258 if (!success)
5259 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005260 vim_free(syn_opt_arg.cont_list);
5261 vim_free(syn_opt_arg.cont_in_list);
5262 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005263 if (not_enough)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005264 semsg(_(e_not_enough_arguments_syntax_region_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005265 else if (illegal || rest == NULL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005266 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005267 }
5268}
5269
5270/*
5271 * A simple syntax group ID comparison function suitable for use in qsort()
5272 */
5273 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005274syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005275{
5276 const short *s1 = v1;
5277 const short *s2 = v2;
5278
5279 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5280}
5281
5282/*
5283 * Combines lists of syntax clusters.
5284 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5285 */
5286 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005287syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005288{
5289 int count1 = 0;
5290 int count2 = 0;
5291 short *g1;
5292 short *g2;
5293 short *clstr = NULL;
5294 int count;
5295 int round;
5296
5297 /*
5298 * Handle degenerate cases.
5299 */
5300 if (*clstr2 == NULL)
5301 return;
5302 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5303 {
5304 if (list_op == CLUSTER_REPLACE)
5305 vim_free(*clstr1);
5306 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5307 *clstr1 = *clstr2;
5308 else
5309 vim_free(*clstr2);
5310 return;
5311 }
5312
5313 for (g1 = *clstr1; *g1; g1++)
5314 ++count1;
5315 for (g2 = *clstr2; *g2; g2++)
5316 ++count2;
5317
5318 /*
5319 * For speed purposes, sort both lists.
5320 */
5321 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5322 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5323
5324 /*
5325 * We proceed in two passes; in round 1, we count the elements to place
5326 * in the new list, and in round 2, we allocate and populate the new
5327 * list. For speed, we use a mergesort-like method, adding the smaller
5328 * of the current elements in each list to the new list.
5329 */
5330 for (round = 1; round <= 2; round++)
5331 {
5332 g1 = *clstr1;
5333 g2 = *clstr2;
5334 count = 0;
5335
5336 /*
5337 * First, loop through the lists until one of them is empty.
5338 */
5339 while (*g1 && *g2)
5340 {
5341 /*
5342 * We always want to add from the first list.
5343 */
5344 if (*g1 < *g2)
5345 {
5346 if (round == 2)
5347 clstr[count] = *g1;
5348 count++;
5349 g1++;
5350 continue;
5351 }
5352 /*
5353 * We only want to add from the second list if we're adding the
5354 * lists.
5355 */
5356 if (list_op == CLUSTER_ADD)
5357 {
5358 if (round == 2)
5359 clstr[count] = *g2;
5360 count++;
5361 }
5362 if (*g1 == *g2)
5363 g1++;
5364 g2++;
5365 }
5366
5367 /*
5368 * Now add the leftovers from whichever list didn't get finished
5369 * first. As before, we only want to add from the second list if
5370 * we're adding the lists.
5371 */
5372 for (; *g1; g1++, count++)
5373 if (round == 2)
5374 clstr[count] = *g1;
5375 if (list_op == CLUSTER_ADD)
5376 for (; *g2; g2++, count++)
5377 if (round == 2)
5378 clstr[count] = *g2;
5379
5380 if (round == 1)
5381 {
5382 /*
5383 * If the group ended up empty, we don't need to allocate any
5384 * space for it.
5385 */
5386 if (count == 0)
5387 {
5388 clstr = NULL;
5389 break;
5390 }
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005391 clstr = ALLOC_MULT(short, count + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005392 if (clstr == NULL)
5393 break;
5394 clstr[count] = 0;
5395 }
5396 }
5397
5398 /*
5399 * Finally, put the new list in place.
5400 */
5401 vim_free(*clstr1);
5402 vim_free(*clstr2);
5403 *clstr1 = clstr;
5404}
5405
5406/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005407 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005408 * If it is not found, 0 is returned.
5409 */
5410 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005411syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005412{
5413 int i;
5414 char_u *name_u;
5415
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005416 // Avoid using stricmp() too much, it's slow on some systems
Bram Moolenaar071d4272004-06-13 20:20:40 +00005417 name_u = vim_strsave_up(name);
5418 if (name_u == NULL)
5419 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005420 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5421 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5422 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005423 break;
5424 vim_free(name_u);
5425 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5426}
5427
5428/*
5429 * Like syn_scl_name2id(), but take a pointer + length argument.
5430 */
5431 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005432syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005433{
5434 char_u *name;
5435 int id = 0;
5436
5437 name = vim_strnsave(linep, len);
5438 if (name != NULL)
5439 {
5440 id = syn_scl_name2id(name);
5441 vim_free(name);
5442 }
5443 return id;
5444}
5445
5446/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005447 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005448 * The argument is a pointer to the name and the length of the name.
5449 * If it doesn't exist yet, a new entry is created.
5450 * Return 0 for failure.
5451 */
5452 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005453syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005454{
5455 int id;
5456 char_u *name;
5457
5458 name = vim_strnsave(pp, len);
5459 if (name == NULL)
5460 return 0;
5461
5462 id = syn_scl_name2id(name);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005463 if (id == 0) // doesn't exist yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00005464 id = syn_add_cluster(name);
5465 else
5466 vim_free(name);
5467 return id;
5468}
5469
5470/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005471 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005472 * "name" must be an allocated string, it will be consumed.
5473 * Return 0 for failure.
5474 */
5475 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005476syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005477{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005478 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005479
5480 /*
5481 * First call for this growarray: init growing array.
5482 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005483 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005484 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005485 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5486 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005487 }
5488
Bram Moolenaar42431a72011-04-01 14:44:59 +02005489 len = curwin->w_s->b_syn_clusters.ga_len;
5490 if (len >= MAX_CLUSTER_ID)
5491 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00005492 emsg(_(e_too_many_syntax_clusters));
Bram Moolenaar42431a72011-04-01 14:44:59 +02005493 vim_free(name);
5494 return 0;
5495 }
5496
Bram Moolenaar071d4272004-06-13 20:20:40 +00005497 /*
5498 * Make room for at least one other cluster entry.
5499 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005500 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005501 {
5502 vim_free(name);
5503 return 0;
5504 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005505
Bram Moolenaara80faa82020-04-12 19:37:17 +02005506 CLEAR_POINTER(&(SYN_CLSTR(curwin->w_s)[len]));
Bram Moolenaar860cae12010-06-05 23:22:07 +02005507 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5508 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5509 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5510 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005511
Bram Moolenaar217ad922005-03-20 22:37:15 +00005512 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005513 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005514 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005515 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005516
Bram Moolenaar071d4272004-06-13 20:20:40 +00005517 return len + SYNID_CLUSTER;
5518}
5519
5520/*
5521 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5522 * [add={groupname},..] [remove={groupname},..]".
5523 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005524 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005525syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005526{
5527 char_u *arg = eap->arg;
5528 char_u *group_name_end;
5529 char_u *rest;
5530 int scl_id;
5531 short *clstr_list;
5532 int got_clstr = FALSE;
5533 int opt_len;
5534 int list_op;
5535
5536 eap->nextcmd = find_nextcmd(arg);
5537 if (eap->skip)
5538 return;
5539
5540 rest = get_group_name(arg, &group_name_end);
5541
5542 if (rest != NULL)
5543 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005544 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5545 if (scl_id == 0)
5546 return;
5547 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005548
5549 for (;;)
5550 {
5551 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005552 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005553 {
5554 opt_len = 3;
5555 list_op = CLUSTER_ADD;
5556 }
5557 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005558 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005559 {
5560 opt_len = 6;
5561 list_op = CLUSTER_SUBTRACT;
5562 }
5563 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005564 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005565 {
5566 opt_len = 8;
5567 list_op = CLUSTER_REPLACE;
5568 }
5569 else
5570 break;
5571
5572 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005573 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005574 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005575 semsg(_(e_invalid_argument_str), rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005576 break;
5577 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005578 if (scl_id >= 0)
5579 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005580 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005581 else
5582 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005583 got_clstr = TRUE;
5584 }
5585
5586 if (got_clstr)
5587 {
Bram Moolenaara4d158b2022-08-14 14:17:45 +01005588 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005589 syn_stack_free_all(curwin->w_s); // Need to recompute all.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005590 }
5591 }
5592
5593 if (!got_clstr)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005594 emsg(_(e_no_cluster_specified));
Bram Moolenaar1966c242020-04-20 22:42:32 +02005595 if (rest == NULL || !ends_excmd2(eap->cmd, rest))
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005596 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005597}
5598
5599/*
5600 * On first call for current buffer: Init growing array.
5601 */
5602 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005603init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005604{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005605 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5606 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005607}
5608
5609/*
5610 * Get one pattern for a ":syntax match" or ":syntax region" command.
5611 * Stores the pattern and program in a synpat_T.
5612 * Returns a pointer to the next argument, or NULL in case of an error.
5613 */
5614 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005615get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005616{
5617 char_u *end;
5618 int *p;
5619 int idx;
5620 char_u *cpo_save;
5621
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005622 // need at least three chars
Bram Moolenaar38219782015-08-11 15:27:13 +02005623 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005624 return NULL;
5625
Bram Moolenaare8c4abb2020-04-02 21:13:25 +02005626 end = skip_regexp(arg + 1, *arg, TRUE);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005627 if (*end != *arg) // end delimiter not found
Bram Moolenaar071d4272004-06-13 20:20:40 +00005628 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005629 semsg(_(e_pattern_delimiter_not_found_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005630 return NULL;
5631 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005632 // store the pattern and compiled regexp program
Bram Moolenaar71ccd032020-06-12 22:59:11 +02005633 if ((ci->sp_pattern = vim_strnsave(arg + 1, end - arg - 1)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005634 return NULL;
5635
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005636 // Make 'cpoptions' empty, to avoid the 'l' flag
Bram Moolenaar071d4272004-06-13 20:20:40 +00005637 cpo_save = p_cpo;
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01005638 p_cpo = empty_option;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005639 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5640 p_cpo = cpo_save;
5641
5642 if (ci->sp_prog == NULL)
5643 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005644 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005645#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005646 syn_clear_time(&ci->sp_time);
5647#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005648
5649 /*
5650 * Check for a match, highlight or region offset.
5651 */
5652 ++end;
5653 do
5654 {
5655 for (idx = SPO_COUNT; --idx >= 0; )
5656 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5657 break;
5658 if (idx >= 0)
5659 {
5660 p = &(ci->sp_offsets[idx]);
5661 if (idx != SPO_LC_OFF)
5662 switch (end[3])
5663 {
5664 case 's': break;
5665 case 'b': break;
5666 case 'e': idx += SPO_COUNT; break;
5667 default: idx = -1; break;
5668 }
5669 if (idx >= 0)
5670 {
5671 ci->sp_off_flags |= (1 << idx);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005672 if (idx == SPO_LC_OFF) // lc=99
Bram Moolenaar071d4272004-06-13 20:20:40 +00005673 {
5674 end += 3;
5675 *p = getdigits(&end);
5676
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005677 // "lc=" offset automatically sets "ms=" offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00005678 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5679 {
5680 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5681 ci->sp_offsets[SPO_MS_OFF] = *p;
5682 }
5683 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005684 else // yy=x+99
Bram Moolenaar071d4272004-06-13 20:20:40 +00005685 {
5686 end += 4;
5687 if (*end == '+')
5688 {
5689 ++end;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005690 *p = getdigits(&end); // positive offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00005691 }
5692 else if (*end == '-')
5693 {
5694 ++end;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005695 *p = -getdigits(&end); // negative offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00005696 }
5697 }
5698 if (*end != ',')
5699 break;
5700 ++end;
5701 }
5702 }
5703 } while (idx >= 0);
5704
Bram Moolenaar1966c242020-04-20 22:42:32 +02005705 if (!ends_excmd2(arg, end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005706 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005707 semsg(_(e_garbage_after_pattern_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005708 return NULL;
5709 }
5710 return skipwhite(end);
5711}
5712
5713/*
5714 * Handle ":syntax sync .." command.
5715 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005716 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005717syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005718{
5719 char_u *arg_start = eap->arg;
5720 char_u *arg_end;
5721 char_u *key = NULL;
5722 char_u *next_arg;
5723 int illegal = FALSE;
5724 int finished = FALSE;
5725 long n;
5726 char_u *cpo_save;
5727
Bram Moolenaar1966c242020-04-20 22:42:32 +02005728 if (ends_excmd2(eap->cmd, arg_start))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005729 {
5730 syn_cmd_list(eap, TRUE);
5731 return;
5732 }
5733
Bram Moolenaar1966c242020-04-20 22:42:32 +02005734 while (!ends_excmd2(eap->cmd, arg_start))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005735 {
5736 arg_end = skiptowhite(arg_start);
5737 next_arg = skipwhite(arg_end);
5738 vim_free(key);
Bram Moolenaardf44a272020-06-07 20:49:05 +02005739 key = vim_strnsave_up(arg_start, arg_end - arg_start);
Bram Moolenaar58bb61c2020-07-10 20:30:12 +02005740 if (key == NULL)
5741 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005742 if (STRCMP(key, "CCOMMENT") == 0)
5743 {
5744 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005745 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar1966c242020-04-20 22:42:32 +02005746 if (!ends_excmd2(eap->cmd, next_arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005747 {
5748 arg_end = skiptowhite(next_arg);
5749 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005750 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005751 (int)(arg_end - next_arg));
5752 next_arg = skipwhite(arg_end);
5753 }
5754 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005755 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005756 }
5757 else if ( STRNCMP(key, "LINES", 5) == 0
5758 || STRNCMP(key, "MINLINES", 8) == 0
5759 || STRNCMP(key, "MAXLINES", 8) == 0
5760 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5761 {
5762 if (key[4] == 'S')
5763 arg_end = key + 6;
5764 else if (key[0] == 'L')
5765 arg_end = key + 11;
5766 else
5767 arg_end = key + 9;
5768 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5769 {
5770 illegal = TRUE;
5771 break;
5772 }
5773 n = getdigits(&arg_end);
5774 if (!eap->skip)
5775 {
5776 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005777 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005778 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005779 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005780 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005781 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005782 }
5783 }
5784 else if (STRCMP(key, "FROMSTART") == 0)
5785 {
5786 if (!eap->skip)
5787 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005788 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5789 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005790 }
5791 }
5792 else if (STRCMP(key, "LINECONT") == 0)
5793 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005794 if (*next_arg == NUL) // missing pattern
Bram Moolenaar2795e212016-01-05 22:04:49 +01005795 {
5796 illegal = TRUE;
5797 break;
5798 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005799 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005800 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005801 emsg(_(e_syntax_sync_line_continuations_pattern_specified_twice));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005802 finished = TRUE;
5803 break;
5804 }
Bram Moolenaare8c4abb2020-04-02 21:13:25 +02005805 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005806 if (*arg_end != *next_arg) // end delimiter not found
Bram Moolenaar071d4272004-06-13 20:20:40 +00005807 {
5808 illegal = TRUE;
5809 break;
5810 }
5811
5812 if (!eap->skip)
5813 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005814 // store the pattern and compiled regexp program
Bram Moolenaar71ccd032020-06-12 22:59:11 +02005815 if ((curwin->w_s->b_syn_linecont_pat =
5816 vim_strnsave(next_arg + 1,
5817 arg_end - next_arg - 1)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005818 {
5819 finished = TRUE;
5820 break;
5821 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005822 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005823
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005824 // Make 'cpoptions' empty, to avoid the 'l' flag
Bram Moolenaar071d4272004-06-13 20:20:40 +00005825 cpo_save = p_cpo;
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01005826 p_cpo = empty_option;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005827 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005828 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005829 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005830#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005831 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5832#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005833
Bram Moolenaar860cae12010-06-05 23:22:07 +02005834 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005835 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01005836 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005837 finished = TRUE;
5838 break;
5839 }
5840 }
5841 next_arg = skipwhite(arg_end + 1);
5842 }
5843 else
5844 {
5845 eap->arg = next_arg;
5846 if (STRCMP(key, "MATCH") == 0)
5847 syn_cmd_match(eap, TRUE);
5848 else if (STRCMP(key, "REGION") == 0)
5849 syn_cmd_region(eap, TRUE);
5850 else if (STRCMP(key, "CLEAR") == 0)
5851 syn_cmd_clear(eap, TRUE);
5852 else
5853 illegal = TRUE;
5854 finished = TRUE;
5855 break;
5856 }
5857 arg_start = next_arg;
5858 }
5859 vim_free(key);
5860 if (illegal)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005861 semsg(_(e_illegal_arguments_str), arg_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005862 else if (!finished)
5863 {
Bram Moolenaar63b91732021-08-05 20:40:03 +02005864 set_nextcmd(eap, arg_start);
Bram Moolenaara4d158b2022-08-14 14:17:45 +01005865 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005866 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005867 }
5868}
5869
5870/*
5871 * Convert a line of highlight group names into a list of group ID numbers.
5872 * "arg" should point to the "contains" or "nextgroup" keyword.
5873 * "arg" is advanced to after the last group name.
5874 * Careful: the argument is modified (NULs added).
5875 * returns FAIL for some error, OK for success.
5876 */
5877 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005878get_id_list(
5879 char_u **arg,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005880 int keylen, // length of keyword
5881 short **list, // where to store the resulting list, if not
5882 // NULL, the list is silently skipped!
Bram Moolenaarde318c52017-01-17 16:27:10 +01005883 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005884{
5885 char_u *p = NULL;
5886 char_u *end;
5887 int round;
5888 int count;
5889 int total_count = 0;
5890 short *retval = NULL;
5891 char_u *name;
5892 regmatch_T regmatch;
5893 int id;
5894 int i;
5895 int failed = FALSE;
5896
5897 /*
5898 * We parse the list twice:
5899 * round == 1: count the number of items, allocate the array.
5900 * round == 2: fill the array with the items.
5901 * In round 1 new groups may be added, causing the number of items to
5902 * grow when a regexp is used. In that case round 1 is done once again.
5903 */
5904 for (round = 1; round <= 2; ++round)
5905 {
5906 /*
5907 * skip "contains"
5908 */
5909 p = skipwhite(*arg + keylen);
5910 if (*p != '=')
5911 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005912 semsg(_(e_missing_equal_sign_str), *arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005913 break;
5914 }
5915 p = skipwhite(p + 1);
Bram Moolenaar1966c242020-04-20 22:42:32 +02005916 if (ends_excmd2(*arg, p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005917 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005918 semsg(_(e_empty_argument_str), *arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005919 break;
5920 }
5921
5922 /*
5923 * parse the arguments after "contains"
5924 */
5925 count = 0;
Bram Moolenaar1966c242020-04-20 22:42:32 +02005926 while (!ends_excmd2(*arg, p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005927 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01005928 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005929 ;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005930 name = alloc(end - p + 3); // leave room for "^$"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005931 if (name == NULL)
5932 {
5933 failed = TRUE;
5934 break;
5935 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005936 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005937 if ( STRCMP(name + 1, "ALLBUT") == 0
5938 || STRCMP(name + 1, "ALL") == 0
5939 || STRCMP(name + 1, "TOP") == 0
5940 || STRCMP(name + 1, "CONTAINED") == 0)
5941 {
5942 if (TOUPPER_ASC(**arg) != 'C')
5943 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005944 semsg(_(e_str_not_allowed_here), name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005945 failed = TRUE;
5946 vim_free(name);
5947 break;
5948 }
5949 if (count != 0)
5950 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005951 semsg(_(e_str_must_be_first_in_contains_list), name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005952 failed = TRUE;
5953 vim_free(name);
5954 break;
5955 }
5956 if (name[1] == 'A')
Bram Moolenaar2e240bd2021-04-14 11:15:08 +02005957 id = SYNID_ALLBUT + current_syn_inc_tag;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005958 else if (name[1] == 'T')
Bram Moolenaar2e240bd2021-04-14 11:15:08 +02005959 {
5960 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
5961 id = curwin->w_s->b_syn_topgrp;
5962 else
5963 id = SYNID_TOP + current_syn_inc_tag;
5964 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005965 else
Bram Moolenaar2e240bd2021-04-14 11:15:08 +02005966 id = SYNID_CONTAINED + current_syn_inc_tag;
5967
Bram Moolenaar071d4272004-06-13 20:20:40 +00005968 }
5969 else if (name[1] == '@')
5970 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01005971 if (skip)
5972 id = -1;
5973 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01005974 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005975 }
5976 else
5977 {
5978 /*
5979 * Handle full group name.
5980 */
5981 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5982 id = syn_check_group(name + 1, (int)(end - p));
5983 else
5984 {
5985 /*
5986 * Handle match of regexp with group names.
5987 */
5988 *name = '^';
5989 STRCAT(name, "$");
5990 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5991 if (regmatch.regprog == NULL)
5992 {
5993 failed = TRUE;
5994 vim_free(name);
5995 break;
5996 }
5997
5998 regmatch.rm_ic = TRUE;
5999 id = 0;
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02006000 for (i = highlight_num_groups(); --i >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006001 {
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02006002 if (vim_regexec(&regmatch, highlight_group_name(i),
Bram Moolenaar071d4272004-06-13 20:20:40 +00006003 (colnr_T)0))
6004 {
6005 if (round == 2)
6006 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006007 // Got more items than expected; can happen
6008 // when adding items that match:
6009 // "contains=a.*b,axb".
6010 // Go back to first round
Bram Moolenaar071d4272004-06-13 20:20:40 +00006011 if (count >= total_count)
6012 {
6013 vim_free(retval);
6014 round = 1;
6015 }
6016 else
6017 retval[count] = i + 1;
6018 }
6019 ++count;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006020 id = -1; // remember that we found one
Bram Moolenaar071d4272004-06-13 20:20:40 +00006021 }
6022 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006023 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006024 }
6025 }
6026 vim_free(name);
6027 if (id == 0)
6028 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00006029 semsg(_(e_unknown_group_name_str), p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006030 failed = TRUE;
6031 break;
6032 }
6033 if (id > 0)
6034 {
6035 if (round == 2)
6036 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006037 // Got more items than expected, go back to first round
Bram Moolenaar071d4272004-06-13 20:20:40 +00006038 if (count >= total_count)
6039 {
6040 vim_free(retval);
6041 round = 1;
6042 }
6043 else
6044 retval[count] = id;
6045 }
6046 ++count;
6047 }
6048 p = skipwhite(end);
6049 if (*p != ',')
6050 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006051 p = skipwhite(p + 1); // skip comma in between arguments
Bram Moolenaar071d4272004-06-13 20:20:40 +00006052 }
6053 if (failed)
6054 break;
6055 if (round == 1)
6056 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006057 retval = ALLOC_MULT(short, count + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006058 if (retval == NULL)
6059 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006060 retval[count] = 0; // zero means end of the list
Bram Moolenaar071d4272004-06-13 20:20:40 +00006061 total_count = count;
6062 }
6063 }
6064
6065 *arg = p;
6066 if (failed || retval == NULL)
6067 {
6068 vim_free(retval);
6069 return FAIL;
6070 }
6071
6072 if (*list == NULL)
6073 *list = retval;
6074 else
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006075 vim_free(retval); // list already found, don't overwrite it
Bram Moolenaar071d4272004-06-13 20:20:40 +00006076
6077 return OK;
6078}
6079
6080/*
6081 * Make a copy of an ID list.
6082 */
6083 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006084copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006085{
6086 int len;
6087 int count;
6088 short *retval;
6089
6090 if (list == NULL)
6091 return NULL;
6092
6093 for (count = 0; list[count]; ++count)
6094 ;
6095 len = (count + 1) * sizeof(short);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006096 retval = alloc(len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006097 if (retval != NULL)
6098 mch_memmove(retval, list, (size_t)len);
6099
6100 return retval;
6101}
6102
6103/*
6104 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6105 * "cur_si" can be NULL if not checking the "containedin" list.
6106 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6107 * the current item.
6108 * This function is called very often, keep it fast!!
6109 */
6110 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006111in_id_list(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006112 stateitem_T *cur_si, // current item or NULL
6113 short *list, // id list
6114 struct sp_syn *ssp, // group id and ":syn include" tag of group
6115 int contained) // group id is contained
Bram Moolenaar071d4272004-06-13 20:20:40 +00006116{
6117 int retval;
6118 short *scl_list;
6119 short item;
6120 short id = ssp->id;
6121 static int depth = 0;
6122 int r;
6123
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006124 // If ssp has a "containedin" list and "cur_si" is in it, return TRUE.
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006125 if (cur_si != NULL && ssp->cont_in_list != NULL
6126 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006127 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006128 // Ignore transparent items without a contains argument. Double check
6129 // that we don't go back past the first one.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006130 while ((cur_si->si_flags & HL_TRANS_CONT)
6131 && cur_si > (stateitem_T *)(current_state.ga_data))
6132 --cur_si;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006133 // cur_si->si_idx is -1 for keywords, these never contain anything.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006134 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006135 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6136 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006137 return TRUE;
6138 }
6139
6140 if (list == NULL)
6141 return FALSE;
6142
6143 /*
6144 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6145 * inside anything. Only allow not-contained groups.
6146 */
6147 if (list == ID_LIST_ALL)
6148 return !contained;
6149
6150 /*
6151 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6152 * contains list. We also require that "id" is at the same ":syn include"
6153 * level as the list.
6154 */
6155 item = *list;
6156 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6157 {
6158 if (item < SYNID_TOP)
6159 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006160 // ALL or ALLBUT: accept all groups in the same file
Bram Moolenaar071d4272004-06-13 20:20:40 +00006161 if (item - SYNID_ALLBUT != ssp->inc_tag)
6162 return FALSE;
6163 }
6164 else if (item < SYNID_CONTAINED)
6165 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006166 // TOP: accept all not-contained groups in the same file
Bram Moolenaar071d4272004-06-13 20:20:40 +00006167 if (item - SYNID_TOP != ssp->inc_tag || contained)
6168 return FALSE;
6169 }
6170 else
6171 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006172 // CONTAINED: accept all contained groups in the same file
Bram Moolenaar071d4272004-06-13 20:20:40 +00006173 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6174 return FALSE;
6175 }
6176 item = *++list;
6177 retval = FALSE;
6178 }
6179 else
6180 retval = TRUE;
6181
6182 /*
6183 * Return "retval" if id is in the contains list.
6184 */
6185 while (item != 0)
6186 {
6187 if (item == id)
6188 return retval;
6189 if (item >= SYNID_CLUSTER)
6190 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006191 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006192 // restrict recursiveness to 30 to avoid an endless loop for a
6193 // cluster that includes itself (indirectly)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006194 if (scl_list != NULL && depth < 30)
6195 {
6196 ++depth;
6197 r = in_id_list(NULL, scl_list, ssp, contained);
6198 --depth;
6199 if (r)
6200 return retval;
6201 }
6202 }
6203 item = *++list;
6204 }
6205 return !retval;
6206}
6207
6208struct subcommand
6209{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006210 char *name; // subcommand name
6211 void (*func)(exarg_T *, int); // function to call
Bram Moolenaar071d4272004-06-13 20:20:40 +00006212};
6213
6214static struct subcommand subcommands[] =
6215{
6216 {"case", syn_cmd_case},
6217 {"clear", syn_cmd_clear},
6218 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006219 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006220 {"enable", syn_cmd_enable},
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006221 {"foldlevel", syn_cmd_foldlevel},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006222 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006223 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006224 {"keyword", syn_cmd_keyword},
6225 {"list", syn_cmd_list},
6226 {"manual", syn_cmd_manual},
6227 {"match", syn_cmd_match},
6228 {"on", syn_cmd_on},
6229 {"off", syn_cmd_off},
6230 {"region", syn_cmd_region},
6231 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006232 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006233 {"sync", syn_cmd_sync},
6234 {"", syn_cmd_list},
6235 {NULL, NULL}
6236};
6237
6238/*
6239 * ":syntax".
6240 * This searches the subcommands[] table for the subcommand name, and calls a
6241 * syntax_subcommand() function to do the rest.
6242 */
6243 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006244ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006245{
6246 char_u *arg = eap->arg;
6247 char_u *subcmd_end;
6248 char_u *subcmd_name;
6249 int i;
6250
6251 syn_cmdlinep = eap->cmdlinep;
6252
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006253 // isolate subcommand name
Bram Moolenaar071d4272004-06-13 20:20:40 +00006254 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6255 ;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02006256 subcmd_name = vim_strnsave(arg, subcmd_end - arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006257 if (subcmd_name != NULL)
6258 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006259 if (eap->skip) // skip error messages for all subcommands
Bram Moolenaar071d4272004-06-13 20:20:40 +00006260 ++emsg_skip;
6261 for (i = 0; ; ++i)
6262 {
6263 if (subcommands[i].name == NULL)
6264 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00006265 semsg(_(e_invalid_syntax_subcommand_str), subcmd_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006266 break;
6267 }
6268 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6269 {
6270 eap->arg = skipwhite(subcmd_end);
6271 (subcommands[i].func)(eap, FALSE);
6272 break;
6273 }
6274 }
6275 vim_free(subcmd_name);
6276 if (eap->skip)
6277 --emsg_skip;
6278 }
6279}
6280
Bram Moolenaar860cae12010-06-05 23:22:07 +02006281 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006282ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006283{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006284 char_u *old_value;
6285 char_u *new_value;
6286
Bram Moolenaar860cae12010-06-05 23:22:07 +02006287 if (curwin->w_s == &curwin->w_buffer->b_s)
6288 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006289 curwin->w_s = ALLOC_ONE(synblock_T);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006290 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006291 hash_init(&curwin->w_s->b_keywtab);
6292 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006293#ifdef FEAT_SPELL
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006294 // TODO: keep the spell checking as it was.
6295 curwin->w_p_spell = FALSE; // No spell checking
Bram Moolenaard1f76af2020-09-13 22:37:34 +02006296 // make sure option values are "empty_option" instead of NULL
Bram Moolenaar860cae12010-06-05 23:22:07 +02006297 clear_string_option(&curwin->w_s->b_p_spc);
6298 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006299 clear_string_option(&curwin->w_s->b_p_spl);
Bram Moolenaard1f76af2020-09-13 22:37:34 +02006300 clear_string_option(&curwin->w_s->b_p_spo);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006301#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006302 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006303 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006304
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006305 // save value of b:current_syntax
Bram Moolenaar1950c352010-06-06 15:21:10 +02006306 old_value = get_var_value((char_u *)"b:current_syntax");
6307 if (old_value != NULL)
6308 old_value = vim_strsave(old_value);
6309
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006310 // Apply the "syntax" autocommand event, this finds and loads the syntax
6311 // file.
Bram Moolenaar860cae12010-06-05 23:22:07 +02006312 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006313
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006314 // move value of b:current_syntax to w:current_syntax
Bram Moolenaar1950c352010-06-06 15:21:10 +02006315 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006316 if (new_value != NULL)
6317 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006318
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006319 // restore value of b:current_syntax
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006320 if (old_value == NULL)
6321 do_unlet((char_u *)"b:current_syntax", TRUE);
6322 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006323 {
6324 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6325 vim_free(old_value);
6326 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006327}
6328
6329 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006330syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006331{
6332 return (win->w_s->b_syn_patterns.ga_len != 0
6333 || win->w_s->b_syn_clusters.ga_len != 0
6334 || win->w_s->b_keywtab.ht_used > 0
6335 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006336}
6337
Bram Moolenaar071d4272004-06-13 20:20:40 +00006338
6339static enum
6340{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006341 EXP_SUBCMD, // expand ":syn" sub-commands
6342 EXP_CASE, // expand ":syn case" arguments
6343 EXP_SPELL, // expand ":syn spell" arguments
bfredlaf9a6002022-08-26 21:58:31 +01006344 EXP_SYNC, // expand ":syn sync" arguments
6345 EXP_CLUSTER // expand ":syn list @cluster" arguments
Bram Moolenaar071d4272004-06-13 20:20:40 +00006346} expand_what;
6347
Bram Moolenaar4f688582007-07-24 12:34:30 +00006348/*
6349 * Reset include_link, include_default, include_none to 0.
6350 * Called when we are done expanding.
6351 */
6352 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006353reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006354{
6355 include_link = include_default = include_none = 0;
6356}
6357
6358/*
6359 * Handle command line completion for :match and :echohl command: Add "None"
6360 * as highlight group.
6361 */
6362 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006363set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006364{
6365 xp->xp_context = EXPAND_HIGHLIGHT;
6366 xp->xp_pattern = arg;
6367 include_none = 1;
6368}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006369
6370/*
6371 * Handle command line completion for :syntax command.
6372 */
6373 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006374set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006375{
6376 char_u *p;
6377
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006378 // Default: expand subcommands
Bram Moolenaar071d4272004-06-13 20:20:40 +00006379 xp->xp_context = EXPAND_SYNTAX;
6380 expand_what = EXP_SUBCMD;
6381 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006382 include_link = 0;
6383 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006384
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006385 // (part of) subcommand already typed
Bram Moolenaar071d4272004-06-13 20:20:40 +00006386 if (*arg != NUL)
6387 {
6388 p = skiptowhite(arg);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006389 if (*p != NUL) // past first word
Bram Moolenaar071d4272004-06-13 20:20:40 +00006390 {
6391 xp->xp_pattern = skipwhite(p);
6392 if (*skiptowhite(xp->xp_pattern) != NUL)
6393 xp->xp_context = EXPAND_NOTHING;
6394 else if (STRNICMP(arg, "case", p - arg) == 0)
6395 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006396 else if (STRNICMP(arg, "spell", p - arg) == 0)
6397 expand_what = EXP_SPELL;
6398 else if (STRNICMP(arg, "sync", p - arg) == 0)
6399 expand_what = EXP_SYNC;
bfredlaf9a6002022-08-26 21:58:31 +01006400 else if (STRNICMP(arg, "list", p - arg) == 0)
6401 {
6402 p = skipwhite(p);
6403 if (*p == '@')
6404 expand_what = EXP_CLUSTER;
6405 else
6406 xp->xp_context = EXPAND_HIGHLIGHT;
6407 }
6408 else if (STRNICMP(arg, "keyword", p - arg) == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00006409 || STRNICMP(arg, "region", p - arg) == 0
bfredlaf9a6002022-08-26 21:58:31 +01006410 || STRNICMP(arg, "match", p - arg) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006411 xp->xp_context = EXPAND_HIGHLIGHT;
6412 else
6413 xp->xp_context = EXPAND_NOTHING;
6414 }
6415 }
6416}
6417
Bram Moolenaar071d4272004-06-13 20:20:40 +00006418/*
6419 * Function given to ExpandGeneric() to obtain the list syntax names for
6420 * expansion.
6421 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006422 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006423get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006424{
bfredlaf9a6002022-08-26 21:58:31 +01006425#define CBUFFER_LEN 256
6426 static char_u cbuffer[CBUFFER_LEN]; // TODO: better solution
6427
Bram Moolenaar2d028392017-01-08 18:28:22 +01006428 switch (expand_what)
6429 {
6430 case EXP_SUBCMD:
6431 return (char_u *)subcommands[idx].name;
6432 case EXP_CASE:
6433 {
6434 static char *case_args[] = {"match", "ignore", NULL};
6435 return (char_u *)case_args[idx];
6436 }
6437 case EXP_SPELL:
6438 {
6439 static char *spell_args[] =
6440 {"toplevel", "notoplevel", "default", NULL};
6441 return (char_u *)spell_args[idx];
6442 }
6443 case EXP_SYNC:
6444 {
6445 static char *sync_args[] =
6446 {"ccomment", "clear", "fromstart",
6447 "linebreaks=", "linecont", "lines=", "match",
6448 "maxlines=", "minlines=", "region", NULL};
6449 return (char_u *)sync_args[idx];
6450 }
bfredlaf9a6002022-08-26 21:58:31 +01006451 case EXP_CLUSTER:
6452 {
6453 if (idx < curwin->w_s->b_syn_clusters.ga_len)
6454 {
6455 vim_snprintf((char *)cbuffer, CBUFFER_LEN, "@%s",
6456 SYN_CLSTR(curwin->w_s)[idx].scl_name);
6457 return cbuffer;
6458 }
6459 else
6460 return NULL;
6461 }
Bram Moolenaar2d028392017-01-08 18:28:22 +01006462 }
6463 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006464}
6465
Bram Moolenaar071d4272004-06-13 20:20:40 +00006466
Bram Moolenaar071d4272004-06-13 20:20:40 +00006467/*
6468 * Function called for expression evaluation: get syntax ID at file position.
6469 */
6470 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006471syn_get_id(
6472 win_T *wp,
6473 long lnum,
6474 colnr_T col,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006475 int trans, // remove transparency
6476 int *spellp, // return: can do spell checking
6477 int keep_state) // keep state of char at "col"
Bram Moolenaar071d4272004-06-13 20:20:40 +00006478{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006479 // When the position is not after the current position and in the same
zeertzjqca7e86c2022-04-16 16:49:24 +01006480 // line of the same window with the same buffer, need to restart parsing.
6481 if (wp != syn_win
6482 || wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006483 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006484 || col < current_col)
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006485 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006486 else if (wp->w_buffer == syn_buf
6487 && lnum == current_lnum
6488 && col > current_col)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006489 // next_match may not be correct when moving around, e.g. with the
6490 // "skip" expression in searchpair()
Bram Moolenaar6773a342016-01-19 20:52:44 +01006491 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006492
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006493 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006494
6495 return (trans ? current_trans_id : current_id);
6496}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006497
Bram Moolenaar860cae12010-06-05 23:22:07 +02006498#if defined(FEAT_CONCEAL) || defined(PROTO)
6499/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006500 * Get extra information about the syntax item. Must be called right after
6501 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006502 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006503 * Returns the current flags.
6504 */
6505 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006506get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006507{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006508 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006509 return current_flags;
6510}
6511
6512/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006513 * Return conceal substitution character
6514 */
6515 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006516syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006517{
6518 return current_sub_char;
6519}
6520#endif
6521
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006522#if defined(FEAT_EVAL) || defined(PROTO)
6523/*
6524 * Return the syntax ID at position "i" in the current stack.
6525 * The caller must have called syn_get_id() before to fill the stack.
6526 * Returns -1 when "i" is out of range.
6527 */
6528 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006529syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006530{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006531 if (i >= current_state.ga_len)
6532 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006533 // Need to invalidate the state, because we didn't properly finish it
6534 // for the last character, "keep_state" was TRUE.
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006535 invalidate_current_state();
6536 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006537 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006538 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006539 return CUR_STATE(i).si_id;
6540}
6541#endif
6542
Bram Moolenaar071d4272004-06-13 20:20:40 +00006543#if defined(FEAT_FOLDING) || defined(PROTO)
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006544 static int
6545syn_cur_foldlevel(void)
6546{
6547 int level = 0;
6548 int i;
6549
6550 for (i = 0; i < current_state.ga_len; ++i)
6551 if (CUR_STATE(i).si_flags & HL_FOLD)
6552 ++level;
6553 return level;
6554}
6555
Bram Moolenaar071d4272004-06-13 20:20:40 +00006556/*
6557 * Function called to get folding level for line "lnum" in window "wp".
6558 */
6559 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006560syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006561{
6562 int level = 0;
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006563 int low_level;
6564 int cur_level;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006565
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006566 // Return quickly when there are no fold items at all.
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006567 if (wp->w_s->b_syn_folditems != 0
6568 && !wp->w_s->b_syn_error
6569# ifdef SYN_TIME_LIMIT
6570 && !wp->w_s->b_syn_slow
6571# endif
6572 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006573 {
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006574 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006575
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006576 // Start with the fold level at the start of the line.
6577 level = syn_cur_foldlevel();
6578
6579 if (wp->w_s->b_syn_foldlevel == SYNFLD_MINIMUM)
6580 {
6581 // Find the lowest fold level that is followed by a higher one.
6582 cur_level = level;
6583 low_level = cur_level;
6584 while (!current_finished)
6585 {
6586 (void)syn_current_attr(FALSE, FALSE, NULL, FALSE);
6587 cur_level = syn_cur_foldlevel();
6588 if (cur_level < low_level)
6589 low_level = cur_level;
6590 else if (cur_level > low_level)
6591 level = low_level;
6592 ++current_col;
6593 }
6594 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006595 }
6596 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006597 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006598 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006599 if (level < 0)
6600 level = 0;
6601 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006602 return level;
6603}
6604#endif
6605
Bram Moolenaar01615492015-02-03 13:00:38 +01006606#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006607/*
6608 * ":syntime".
6609 */
6610 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006611ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006612{
6613 if (STRCMP(eap->arg, "on") == 0)
6614 syn_time_on = TRUE;
6615 else if (STRCMP(eap->arg, "off") == 0)
6616 syn_time_on = FALSE;
6617 else if (STRCMP(eap->arg, "clear") == 0)
6618 syntime_clear();
6619 else if (STRCMP(eap->arg, "report") == 0)
6620 syntime_report();
6621 else
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00006622 semsg(_(e_invalid_argument_str), eap->arg);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006623}
6624
6625 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006626syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006627{
6628 profile_zero(&st->total);
6629 profile_zero(&st->slowest);
6630 st->count = 0;
6631 st->match = 0;
6632}
6633
6634/*
6635 * Clear the syntax timing for the current buffer.
6636 */
6637 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006638syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006639{
6640 int idx;
6641 synpat_T *spp;
6642
6643 if (!syntax_present(curwin))
6644 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006645 msg(_(msg_no_items));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006646 return;
6647 }
6648 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6649 {
6650 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6651 syn_clear_time(&spp->sp_time);
6652 }
6653}
6654
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006655/*
6656 * Function given to ExpandGeneric() to obtain the possible arguments of the
6657 * ":syntime {on,off,clear,report}" command.
6658 */
6659 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006660get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006661{
6662 switch (idx)
6663 {
6664 case 0: return (char_u *)"on";
6665 case 1: return (char_u *)"off";
6666 case 2: return (char_u *)"clear";
6667 case 3: return (char_u *)"report";
6668 }
6669 return NULL;
6670}
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006671
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006672typedef struct
6673{
6674 proftime_T total;
6675 int count;
6676 int match;
6677 proftime_T slowest;
6678 proftime_T average;
6679 int id;
6680 char_u *pattern;
6681} time_entry_T;
6682
6683 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006684syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006685{
6686 const time_entry_T *s1 = v1;
6687 const time_entry_T *s2 = v2;
6688
6689 return profile_cmp(&s1->total, &s2->total);
6690}
6691
6692/*
6693 * Clear the syntax timing for the current buffer.
6694 */
6695 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006696syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006697{
6698 int idx;
6699 synpat_T *spp;
Bram Moolenaar81319672020-11-25 11:47:39 +01006700# if defined(FEAT_RELTIME) && defined(FEAT_FLOAT)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006701 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006702# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006703 int len;
6704 proftime_T total_total;
6705 int total_count = 0;
6706 garray_T ga;
6707 time_entry_T *p;
6708
6709 if (!syntax_present(curwin))
6710 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006711 msg(_(msg_no_items));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006712 return;
6713 }
6714
6715 ga_init2(&ga, sizeof(time_entry_T), 50);
6716 profile_zero(&total_total);
6717 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6718 {
6719 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6720 if (spp->sp_time.count > 0)
6721 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006722 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006723 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6724 p->total = spp->sp_time.total;
6725 profile_add(&total_total, &spp->sp_time.total);
6726 p->count = spp->sp_time.count;
6727 p->match = spp->sp_time.match;
6728 total_count += spp->sp_time.count;
6729 p->slowest = spp->sp_time.slowest;
Bram Moolenaar81319672020-11-25 11:47:39 +01006730# if defined(FEAT_RELTIME) && defined(FEAT_FLOAT)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006731 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6732 p->average = tm;
6733# endif
6734 p->id = spp->sp_syn.id;
6735 p->pattern = spp->sp_pattern;
6736 ++ga.ga_len;
6737 }
6738 }
6739
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006740 // Sort on total time. Skip if there are no items to avoid passing NULL
6741 // pointer to qsort().
Bram Moolenaara2162552017-01-08 17:46:20 +01006742 if (ga.ga_len > 1)
6743 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006744 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006745
Bram Moolenaar32526b32019-01-19 17:43:09 +01006746 msg_puts_title(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6747 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006748 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6749 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006750 p = ((time_entry_T *)ga.ga_data) + idx;
6751
Bram Moolenaar32526b32019-01-19 17:43:09 +01006752 msg_puts(profile_msg(&p->total));
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006753 msg_puts(" "); // make sure there is always a separating space
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006754 msg_advance(13);
6755 msg_outnum(p->count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006756 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006757 msg_advance(20);
6758 msg_outnum(p->match);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006759 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006760 msg_advance(26);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006761 msg_puts(profile_msg(&p->slowest));
6762 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006763 msg_advance(38);
6764# ifdef FEAT_FLOAT
Bram Moolenaar32526b32019-01-19 17:43:09 +01006765 msg_puts(profile_msg(&p->average));
6766 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006767# endif
6768 msg_advance(50);
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02006769 msg_outtrans(highlight_group_name(p->id - 1));
Bram Moolenaar32526b32019-01-19 17:43:09 +01006770 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006771
6772 msg_advance(69);
6773 if (Columns < 80)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006774 len = 20; // will wrap anyway
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006775 else
6776 len = Columns - 70;
6777 if (len > (int)STRLEN(p->pattern))
6778 len = (int)STRLEN(p->pattern);
6779 msg_outtrans_len(p->pattern, len);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006780 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006781 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006782 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006783 if (!got_int)
6784 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006785 msg_puts("\n");
6786 msg_puts(profile_msg(&total_total));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006787 msg_advance(13);
6788 msg_outnum(total_count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006789 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006790 }
6791}
6792#endif
6793
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006794#endif // FEAT_SYN_HL