blob: 02120529f3447e56cc9e6e9c27185e0d88c5e45f [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;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000596 int found_flags = 0;
597 int found_match_idx = 0;
598 linenr_T found_current_lnum = 0;
599 int found_current_col= 0;
600 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000601 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000602
603 /*
604 * Clear any current state that might be hanging around.
605 */
606 invalidate_current_state();
607
608 /*
609 * Start at least "minlines" back. Default starting point for parsing is
610 * there.
611 * Start further back, to avoid that scrolling backwards will result in
612 * resyncing for every line. Now it resyncs only one out of N lines,
613 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
614 * Watch out for overflow when minlines is MAXLNUM.
615 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200616 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000617 start_lnum = 1;
618 else
619 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200620 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000621 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200622 else if (syn_block->b_syn_sync_minlines < 10)
623 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000624 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200625 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
626 if (syn_block->b_syn_sync_maxlines != 0
627 && lnum > syn_block->b_syn_sync_maxlines)
628 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000629 if (lnum >= start_lnum)
630 start_lnum = 1;
631 else
632 start_lnum -= lnum;
633 }
634 current_lnum = start_lnum;
635
636 /*
637 * 1. Search backwards for the end of a C-style comment.
638 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200639 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000640 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100641 // Need to make syn_buf the current buffer for a moment, to be able to
642 // use find_start_comment().
Bram Moolenaar071d4272004-06-13 20:20:40 +0000643 curwin_save = curwin;
644 curwin = wp;
645 curbuf_save = curbuf;
646 curbuf = syn_buf;
647
648 /*
649 * Skip lines that end in a backslash.
650 */
651 for ( ; start_lnum > 1; --start_lnum)
652 {
zeertzjq94b7c322024-03-12 21:50:32 +0100653 char_u *l = ml_get(start_lnum - 1);
654
655 if (*l == NUL || *(l + ml_get_len(start_lnum - 1) - 1) != '\\')
Bram Moolenaar071d4272004-06-13 20:20:40 +0000656 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{
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000858 if (syn_block->b_syn_isk == empty_option)
859 return;
860
861 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
862 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab, (size_t)32);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100863}
864
865 static void
866restore_chartab(char_u *chartab)
867{
868 if (syn_win->w_s->b_syn_isk != empty_option)
869 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
870}
871
Bram Moolenaar071d4272004-06-13 20:20:40 +0000872/*
873 * Return TRUE if the line-continuation pattern matches in line "lnum".
874 */
875 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100876syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000877{
878 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100879 int r;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100880 char_u buf_chartab[32]; // chartab array for syn iskyeyword
Bram Moolenaar071d4272004-06-13 20:20:40 +0000881
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000882 if (syn_block->b_syn_linecont_prog == NULL)
883 return FALSE;
884
885 // use syntax iskeyword option
886 save_chartab(buf_chartab);
887 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
888 regmatch.regprog = syn_block->b_syn_linecont_prog;
889 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
890 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
891 syn_block->b_syn_linecont_prog = regmatch.regprog;
892 restore_chartab(buf_chartab);
893 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000894}
895
896/*
897 * Prepare the current state for the start of a line.
898 */
899 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100900syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000901{
902 current_finished = FALSE;
903 current_col = 0;
904
905 /*
906 * Need to update the end of a start/skip/end that continues from the
907 * previous line and regions that have "keepend".
908 */
909 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +0200910 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000911 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +0200912 check_state_ends();
913 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000914
915 next_match_idx = -1;
916 ++current_line_id;
Bram Moolenaarea20de82017-06-24 22:52:24 +0200917#ifdef FEAT_CONCEAL
Bram Moolenaarcc0750d2017-06-24 22:29:24 +0200918 next_seqnr = 1;
Bram Moolenaarea20de82017-06-24 22:52:24 +0200919#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000920}
921
922/*
923 * Check for items in the stack that need their end updated.
924 * When "startofline" is TRUE the last item is always updated.
925 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
926 */
927 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100928syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000929{
930 stateitem_T *cur_si;
931 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000932 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000933
934 if (startofline)
935 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100936 // Check for a match carried over from a previous line with a
937 // contained region. The match ends as soon as the region ends.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000938 for (i = 0; i < current_state.ga_len; ++i)
939 {
940 cur_si = &CUR_STATE(i);
941 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +0200942 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +0000943 == SPTYPE_MATCH
944 && cur_si->si_m_endpos.lnum < current_lnum)
945 {
946 cur_si->si_flags |= HL_MATCHCONT;
947 cur_si->si_m_endpos.lnum = 0;
948 cur_si->si_m_endpos.col = 0;
949 cur_si->si_h_endpos = cur_si->si_m_endpos;
950 cur_si->si_ends = TRUE;
951 }
952 }
953 }
954
955 /*
956 * Need to update the end of a start/skip/end that continues from the
957 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000958 * influence contained items. If we've just removed "extend"
959 * (startofline == 0) then we should update ends of normal regions
960 * contained inside "keepend" because "extend" could have extended
961 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000962 * Then check for items ending in column 0.
963 */
964 i = current_state.ga_len - 1;
965 if (keepend_level >= 0)
966 for ( ; i > keepend_level; --i)
967 if (CUR_STATE(i).si_flags & HL_EXTEND)
968 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000969
970 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000971 for ( ; i < current_state.ga_len; ++i)
972 {
973 cur_si = &CUR_STATE(i);
974 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000975 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000976 || (i == current_state.ga_len - 1 && startofline))
977 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100978 cur_si->si_h_startpos.col = 0; // start highl. in col 0
Bram Moolenaar071d4272004-06-13 20:20:40 +0000979 cur_si->si_h_startpos.lnum = current_lnum;
980
981 if (!(cur_si->si_flags & HL_MATCHCONT))
982 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000983
984 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
985 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000986 }
987 }
988 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000989}
990
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100991/////////////////////////////////////////
992// Handling of the state stack cache.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000993
994/*
995 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
996 *
997 * To speed up syntax highlighting, the state stack for the start of some
998 * lines is cached. These entries can be used to start parsing at that point.
999 *
1000 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1001 * valid entries. b_sst_first points to the first one, then follow sst_next.
1002 * The entries are sorted on line number. The first entry is often for line 2
1003 * (line 1 always starts with an empty stack).
1004 * There is also a list for free entries. This construction is used to avoid
1005 * having to allocate and free memory blocks too often.
1006 *
1007 * When making changes to the buffer, this is logged in b_mod_*. When calling
1008 * update_screen() to update the display, it will call
1009 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1010 * entries. The entries which are inside the changed area are removed,
1011 * because they must be recomputed. Entries below the changed have their line
1012 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1013 * set to indicate that a check must be made if the changed lines would change
1014 * the cached entry.
1015 *
1016 * When later displaying lines, an entry is stored for each line. Displayed
1017 * lines are likely to be displayed again, in which case the state at the
1018 * start of the line is needed.
1019 * For not displayed lines, an entry is stored for every so many lines. These
1020 * entries will be used e.g., when scrolling backwards. The distance between
1021 * entries depends on the number of lines in the buffer. For small buffers
1022 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1023 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1024 */
1025
Bram Moolenaar860cae12010-06-05 23:22:07 +02001026 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001027syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001028{
1029 synstate_T *p;
1030
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001031 if (block->b_sst_array == NULL)
1032 return;
1033
1034 FOR_ALL_SYNSTATES(block, p)
1035 clear_syn_state(p);
1036 VIM_CLEAR(block->b_sst_array);
1037 block->b_sst_first = NULL;
1038 block->b_sst_len = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001039}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001040/*
1041 * Free b_sst_array[] for buffer "buf".
1042 * Used when syntax items changed to force resyncing everywhere.
1043 */
1044 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001045syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001046{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001047#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001048 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001049#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001050
Bram Moolenaar860cae12010-06-05 23:22:07 +02001051 syn_stack_free_block(block);
1052
Bram Moolenaar071d4272004-06-13 20:20:40 +00001053#ifdef FEAT_FOLDING
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001054 // When using "syntax" fold method, must update all folds.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001055 FOR_ALL_WINDOWS(wp)
1056 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001057 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001058 foldUpdateAll(wp);
1059 }
1060#endif
1061}
1062
1063/*
1064 * Allocate the syntax state stack for syn_buf when needed.
1065 * If the number of entries in b_sst_array[] is much too big or a bit too
1066 * small, reallocate it.
1067 * Also used to allocate b_sst_array[] for the first time.
1068 */
1069 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001070syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001071{
1072 long len;
1073 synstate_T *to, *from;
1074 synstate_T *sstp;
1075
1076 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1077 if (len < SST_MIN_ENTRIES)
1078 len = SST_MIN_ENTRIES;
1079 else if (len > SST_MAX_ENTRIES)
1080 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001081 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001082 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001083 // Allocate 50% too much, to avoid reallocating too often.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001084 len = syn_buf->b_ml.ml_line_count;
1085 len = (len + len / 2) / SST_DIST + Rows * 2;
1086 if (len < SST_MIN_ENTRIES)
1087 len = SST_MIN_ENTRIES;
1088 else if (len > SST_MAX_ENTRIES)
1089 len = SST_MAX_ENTRIES;
1090
Bram Moolenaar860cae12010-06-05 23:22:07 +02001091 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001092 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001093 // When shrinking the array, cleanup the existing stack.
1094 // Make sure that all valid entries fit in the new array.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001095 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001096 && syn_stack_cleanup())
1097 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001098 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1099 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001100 }
1101
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001102 sstp = ALLOC_CLEAR_MULT(synstate_T, len);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001103 if (sstp == NULL) // out of memory!
Bram Moolenaar071d4272004-06-13 20:20:40 +00001104 return;
1105
1106 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001107 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001108 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001109 // Move the states from the old array to the new one.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001110 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001111 from = from->sst_next)
1112 {
1113 ++to;
1114 *to = *from;
1115 to->sst_next = to + 1;
1116 }
1117 }
1118 if (to != sstp - 1)
1119 {
1120 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001121 syn_block->b_sst_first = sstp;
1122 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001123 }
1124 else
1125 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001126 syn_block->b_sst_first = NULL;
1127 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001128 }
1129
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001130 // Create the list of free entries.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001131 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001132 while (++to < sstp + len)
1133 to->sst_next = to + 1;
1134 (sstp + len - 1)->sst_next = NULL;
1135
Bram Moolenaar860cae12010-06-05 23:22:07 +02001136 vim_free(syn_block->b_sst_array);
1137 syn_block->b_sst_array = sstp;
1138 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001139 }
1140}
1141
1142/*
1143 * Check for changes in a buffer to affect stored syntax states. Uses the
1144 * b_mod_* fields.
1145 * Called from update_screen(), before screen is being updated, once for each
1146 * displayed buffer.
1147 */
1148 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001149syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001150{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001151 win_T *wp;
1152
1153 syn_stack_apply_changes_block(&buf->b_s, buf);
1154
1155 FOR_ALL_WINDOWS(wp)
1156 {
1157 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1158 syn_stack_apply_changes_block(wp->w_s, buf);
1159 }
1160}
1161
1162 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001163syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001164{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001165 synstate_T *p, *prev, *np;
1166 linenr_T n;
1167
Bram Moolenaar071d4272004-06-13 20:20:40 +00001168 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001169 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001170 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001171 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001172 {
1173 n = p->sst_lnum + buf->b_mod_xlines;
1174 if (n <= buf->b_mod_bot)
1175 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001176 // this state is inside the changed area, remove it
Bram Moolenaar071d4272004-06-13 20:20:40 +00001177 np = p->sst_next;
1178 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001179 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001180 else
1181 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001182 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001183 p = np;
1184 continue;
1185 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001186 // This state is below the changed area. Remember the line
1187 // that needs to be parsed before this entry can be made valid
1188 // again.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001189 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1190 {
1191 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1192 p->sst_change_lnum += buf->b_mod_xlines;
1193 else
1194 p->sst_change_lnum = buf->b_mod_top;
1195 }
1196 if (p->sst_change_lnum == 0
1197 || p->sst_change_lnum < buf->b_mod_bot)
1198 p->sst_change_lnum = buf->b_mod_bot;
1199
1200 p->sst_lnum = n;
1201 }
1202 prev = p;
1203 p = p->sst_next;
1204 }
1205}
1206
1207/*
1208 * Reduce the number of entries in the state stack for syn_buf.
1209 * Returns TRUE if at least one entry was freed.
1210 */
1211 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001212syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001213{
1214 synstate_T *p, *prev;
1215 disptick_T tick;
1216 int above;
1217 int dist;
1218 int retval = FALSE;
1219
Bram Moolenaar95892c22018-09-28 22:26:54 +02001220 if (syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001221 return retval;
1222
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001223 // Compute normal distance between non-displayed entries.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001224 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001225 dist = 999999;
1226 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001227 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001228
1229 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001230 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001231 * be removed. Set "above" when the "tick" for the oldest entry is above
1232 * "b_sst_lasttick" (the display tick wraps around).
1233 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001234 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001235 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001236 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001237 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1238 {
1239 if (prev->sst_lnum + dist > p->sst_lnum)
1240 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001241 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001242 {
1243 if (!above || p->sst_tick < tick)
1244 tick = p->sst_tick;
1245 above = TRUE;
1246 }
1247 else if (!above && p->sst_tick < tick)
1248 tick = p->sst_tick;
1249 }
1250 }
1251
1252 /*
1253 * Go through the list to make the entries for the oldest tick at an
1254 * interval of several lines.
1255 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001256 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001257 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1258 {
1259 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1260 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001261 // Move this entry from used list to free list
Bram Moolenaar071d4272004-06-13 20:20:40 +00001262 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001263 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001264 p = prev;
1265 retval = TRUE;
1266 }
1267 }
1268 return retval;
1269}
1270
1271/*
1272 * Free the allocated memory for a syn_state item.
1273 * Move the entry into the free list.
1274 */
1275 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001276syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001277{
1278 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001279 p->sst_next = block->b_sst_firstfree;
1280 block->b_sst_firstfree = p;
1281 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001282}
1283
1284/*
1285 * Find an entry in the list of state stacks at or before "lnum".
1286 * Returns NULL when there is no entry or the first entry is after "lnum".
1287 */
1288 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001289syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001290{
1291 synstate_T *p, *prev;
1292
1293 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001294 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001295 {
1296 if (p->sst_lnum == lnum)
1297 return p;
1298 if (p->sst_lnum > lnum)
1299 break;
1300 }
1301 return prev;
1302}
1303
1304/*
1305 * Try saving the current state in b_sst_array[].
1306 * The current state must be valid for the start of the current_lnum line!
1307 */
1308 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001309store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001310{
1311 int i;
1312 synstate_T *p;
1313 bufstate_T *bp;
1314 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001315 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001316
1317 /*
1318 * If the current state contains a start or end pattern that continues
1319 * from the previous line, we can't use it. Don't store it then.
1320 */
1321 for (i = current_state.ga_len - 1; i >= 0; --i)
1322 {
1323 cur_si = &CUR_STATE(i);
1324 if (cur_si->si_h_startpos.lnum >= current_lnum
1325 || cur_si->si_m_endpos.lnum >= current_lnum
1326 || cur_si->si_h_endpos.lnum >= current_lnum
1327 || (cur_si->si_end_idx
1328 && cur_si->si_eoe_pos.lnum >= current_lnum))
1329 break;
1330 }
1331 if (i >= 0)
1332 {
1333 if (sp != NULL)
1334 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001335 // find "sp" in the list and remove it
Bram Moolenaar860cae12010-06-05 23:22:07 +02001336 if (syn_block->b_sst_first == sp)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001337 // it's the first entry
Bram Moolenaar860cae12010-06-05 23:22:07 +02001338 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001339 else
1340 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001341 // find the entry just before this one to adjust sst_next
Bram Moolenaar00d253e2020-04-06 22:13:01 +02001342 FOR_ALL_SYNSTATES(syn_block, p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001343 if (p->sst_next == sp)
1344 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001345 if (p != NULL) // just in case
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001346 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001347 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001348 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001349 sp = NULL;
1350 }
1351 }
1352 else if (sp == NULL || sp->sst_lnum != current_lnum)
1353 {
1354 /*
1355 * Add a new entry
1356 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001357 // If no free items, cleanup the array first.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001358 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001359 {
1360 (void)syn_stack_cleanup();
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001361 // "sp" may have been moved to the freelist now
Bram Moolenaar071d4272004-06-13 20:20:40 +00001362 sp = syn_stack_find_entry(current_lnum);
1363 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001364 // Still no free items? Must be a strange problem...
Bram Moolenaar860cae12010-06-05 23:22:07 +02001365 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001366 sp = NULL;
1367 else
1368 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001369 // Take the first item from the free list and put it in the used
1370 // list, after *sp
Bram Moolenaar860cae12010-06-05 23:22:07 +02001371 p = syn_block->b_sst_firstfree;
1372 syn_block->b_sst_firstfree = p->sst_next;
1373 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001374 if (sp == NULL)
1375 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001376 // Insert in front of the list
Bram Moolenaar860cae12010-06-05 23:22:07 +02001377 p->sst_next = syn_block->b_sst_first;
1378 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001379 }
1380 else
1381 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001382 // insert in list after *sp
Bram Moolenaar071d4272004-06-13 20:20:40 +00001383 p->sst_next = sp->sst_next;
1384 sp->sst_next = p;
1385 }
1386 sp = p;
1387 sp->sst_stacksize = 0;
1388 sp->sst_lnum = current_lnum;
1389 }
1390 }
1391 if (sp != NULL)
1392 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001393 // When overwriting an existing state stack, clear it first
Bram Moolenaar071d4272004-06-13 20:20:40 +00001394 clear_syn_state(sp);
1395 sp->sst_stacksize = current_state.ga_len;
1396 if (current_state.ga_len > SST_FIX_STATES)
1397 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001398 // Need to clear it, might be something remaining from when the
1399 // length was less than SST_FIX_STATES.
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001400 ga_init2(&sp->sst_union.sst_ga, sizeof(bufstate_T), 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001401 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1402 sp->sst_stacksize = 0;
1403 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001404 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001405 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1406 }
1407 else
1408 bp = sp->sst_union.sst_stack;
1409 for (i = 0; i < sp->sst_stacksize; ++i)
1410 {
1411 bp[i].bs_idx = CUR_STATE(i).si_idx;
1412 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001413#ifdef FEAT_CONCEAL
1414 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1415 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1416#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001417 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1418 }
1419 sp->sst_next_flags = current_next_flags;
1420 sp->sst_next_list = current_next_list;
1421 sp->sst_tick = display_tick;
1422 sp->sst_change_lnum = 0;
1423 }
1424 current_state_stored = TRUE;
1425 return sp;
1426}
1427
1428/*
1429 * Copy a state stack from "from" in b_sst_array[] to current_state;
1430 */
1431 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001432load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001433{
1434 int i;
1435 bufstate_T *bp;
1436
1437 clear_current_state();
1438 validate_current_state();
1439 keepend_level = -1;
1440 if (from->sst_stacksize
Yegappan Lakshmananfadc02a2023-01-27 21:03:12 +00001441 && ga_grow(&current_state, from->sst_stacksize) == OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001442 {
1443 if (from->sst_stacksize > SST_FIX_STATES)
1444 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1445 else
1446 bp = from->sst_union.sst_stack;
1447 for (i = 0; i < from->sst_stacksize; ++i)
1448 {
1449 CUR_STATE(i).si_idx = bp[i].bs_idx;
1450 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001451#ifdef FEAT_CONCEAL
1452 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1453 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1454#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001455 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1456 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1457 keepend_level = i;
1458 CUR_STATE(i).si_ends = FALSE;
1459 CUR_STATE(i).si_m_lnum = 0;
1460 if (CUR_STATE(i).si_idx >= 0)
1461 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001462 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001463 else
1464 CUR_STATE(i).si_next_list = NULL;
1465 update_si_attr(i);
1466 }
1467 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001468 }
1469 current_next_list = from->sst_next_list;
1470 current_next_flags = from->sst_next_flags;
1471 current_lnum = from->sst_lnum;
1472}
1473
1474/*
1475 * Compare saved state stack "*sp" with the current state.
1476 * Return TRUE when they are equal.
1477 */
1478 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001479syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001480{
1481 int i, j;
1482 bufstate_T *bp;
1483 reg_extmatch_T *six, *bsx;
1484
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001485 // First a quick check if the stacks have the same size end nextlist.
zeertzjq101d57b2022-07-31 18:34:32 +01001486 if (sp->sst_stacksize != current_state.ga_len
1487 || sp->sst_next_list != current_next_list)
1488 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001489
zeertzjq101d57b2022-07-31 18:34:32 +01001490 // Need to compare all states on both stacks.
1491 if (sp->sst_stacksize > SST_FIX_STATES)
1492 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1493 else
1494 bp = sp->sst_union.sst_stack;
1495
1496 for (i = current_state.ga_len; --i >= 0; )
1497 {
1498 // If the item has another index the state is different.
1499 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1500 break;
1501 if (bp[i].bs_extmatch == CUR_STATE(i).si_extmatch)
1502 continue;
1503 // When the extmatch pointers are different, the strings in them can
1504 // still be the same. Check if the extmatch references are equal.
1505 bsx = bp[i].bs_extmatch;
1506 six = CUR_STATE(i).si_extmatch;
1507 // If one of the extmatch pointers is NULL the states are different.
1508 if (bsx == NULL || six == NULL)
1509 break;
1510 for (j = 0; j < NSUBEXP; ++j)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001511 {
zeertzjq101d57b2022-07-31 18:34:32 +01001512 // Check each referenced match string. They must all be equal.
1513 if (bsx->matches[j] != six->matches[j])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001514 {
zeertzjq101d57b2022-07-31 18:34:32 +01001515 // If the pointer is different it can still be the same text.
1516 // Compare the strings, ignore case when the start item has the
1517 // sp_ic flag set.
1518 if (bsx->matches[j] == NULL || six->matches[j] == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001519 break;
zeertzjq101d57b2022-07-31 18:34:32 +01001520 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
1521 ? MB_STRICMP(bsx->matches[j], six->matches[j]) != 0
1522 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001523 break;
1524 }
1525 }
zeertzjq101d57b2022-07-31 18:34:32 +01001526 if (j != NSUBEXP)
1527 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001528 }
zeertzjq101d57b2022-07-31 18:34:32 +01001529 return i < 0 ? TRUE : FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001530}
1531
1532/*
1533 * We stop parsing syntax above line "lnum". If the stored state at or below
1534 * this line depended on a change before it, it now depends on the line below
1535 * the last parsed line.
1536 * The window looks like this:
1537 * line which changed
1538 * displayed line
1539 * displayed line
1540 * lnum -> line below window
1541 */
1542 void
Bram Moolenaar0abd6cf2022-10-14 17:04:09 +01001543syntax_end_parsing(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001544{
1545 synstate_T *sp;
1546
Bram Moolenaar0abd6cf2022-10-14 17:04:09 +01001547 if (syn_block != wp->w_s)
1548 return; // not the right window
Bram Moolenaar071d4272004-06-13 20:20:40 +00001549 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;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002778 int had_match = FALSE;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002779 char_u buf_chartab[32]; // chartab array for syn option iskyeyword
Bram Moolenaar071d4272004-06-13 20:20:40 +00002780
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002781 // just in case we are invoked for a keyword
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002782 if (idx < 0)
2783 return;
2784
Bram Moolenaar071d4272004-06-13 20:20:40 +00002785 /*
2786 * Check for being called with a START pattern.
2787 * Can happen with a match that continues to the next line, because it
2788 * contained a region.
2789 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002790 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002791 if (spp->sp_type != SPTYPE_START)
2792 {
2793 *hl_endpos = *startpos;
2794 return;
2795 }
2796
2797 /*
2798 * Find the SKIP or first END pattern after the last START pattern.
2799 */
2800 for (;;)
2801 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002802 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002803 if (spp->sp_type != SPTYPE_START)
2804 break;
2805 ++idx;
2806 }
2807
2808 /*
2809 * Lookup the SKIP pattern (if present)
2810 */
2811 if (spp->sp_type == SPTYPE_SKIP)
2812 {
2813 spp_skip = spp;
2814 ++idx;
2815 }
2816 else
2817 spp_skip = NULL;
2818
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002819 // Setup external matches for syn_regexec().
Bram Moolenaar071d4272004-06-13 20:20:40 +00002820 unref_extmatch(re_extmatch_in);
2821 re_extmatch_in = ref_extmatch(start_ext);
2822
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002823 matchcol = startpos->col; // start looking for a match at sstart
2824 start_idx = idx; // remember the first END pattern.
2825 best_regmatch.startpos[0].col = 0; // avoid compiler warning
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002826
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002827 // use syntax iskeyword option
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002828 save_chartab(buf_chartab);
2829
Bram Moolenaar071d4272004-06-13 20:20:40 +00002830 for (;;)
2831 {
2832 /*
2833 * Find end pattern that matches first after "matchcol".
2834 */
2835 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002836 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002837 {
2838 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002839 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002840
Bram Moolenaar860cae12010-06-05 23:22:07 +02002841 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002842 if (spp->sp_type != SPTYPE_END) // past last END pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00002843 break;
2844 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2845 if (lc_col < 0)
2846 lc_col = 0;
2847
2848 regmatch.rmm_ic = spp->sp_ic;
2849 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002850 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
2851 IF_SYN_TIME(&spp->sp_time));
2852 spp->sp_prog = regmatch.regprog;
2853 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002854 {
2855 if (best_idx == -1 || regmatch.startpos[0].col
2856 < best_regmatch.startpos[0].col)
2857 {
2858 best_idx = idx;
2859 best_regmatch.startpos[0] = regmatch.startpos[0];
2860 best_regmatch.endpos[0] = regmatch.endpos[0];
2861 }
2862 }
2863 }
2864
2865 /*
2866 * If all end patterns have been tried, and there is no match, the
2867 * item continues until end-of-line.
2868 */
2869 if (best_idx == -1)
2870 break;
2871
2872 /*
2873 * If the skip pattern matches before the end pattern,
2874 * continue searching after the skip pattern.
2875 */
2876 if (spp_skip != NULL)
2877 {
2878 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002879 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002880
2881 if (lc_col < 0)
2882 lc_col = 0;
2883 regmatch.rmm_ic = spp_skip->sp_ic;
2884 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002885 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
2886 IF_SYN_TIME(&spp_skip->sp_time));
2887 spp_skip->sp_prog = regmatch.regprog;
2888 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00002889 <= best_regmatch.startpos[0].col)
2890 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01002891 int line_len;
2892
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002893 // Add offset to skip pattern match
Bram Moolenaar071d4272004-06-13 20:20:40 +00002894 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2895
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002896 // If the skip pattern goes on to the next line, there is no
2897 // match with an end pattern in this line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002898 if (pos.lnum > startpos->lnum)
2899 break;
2900
zeertzjq94b7c322024-03-12 21:50:32 +01002901 line_len = ml_get_buf_len(syn_buf, startpos->lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002902
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002903 // take care of an empty match or negative offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00002904 if (pos.col <= matchcol)
2905 ++matchcol;
2906 else if (pos.col <= regmatch.endpos[0].col)
2907 matchcol = pos.col;
2908 else
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002909 // Be careful not to jump over the NUL at the end-of-line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002910 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01002911 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002912 ++matchcol)
2913 ;
2914
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002915 // if the skip pattern includes end-of-line, break here
Bram Moolenaar04bff882016-01-05 20:46:16 +01002916 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002917 break;
2918
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002919 continue; // start with first end pattern again
Bram Moolenaar071d4272004-06-13 20:20:40 +00002920 }
2921 }
2922
2923 /*
2924 * Match from start pattern to end pattern.
2925 * Correct for match and highlight offset of end pattern.
2926 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002927 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002928 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002929 // can't end before the start
Bram Moolenaar071d4272004-06-13 20:20:40 +00002930 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
2931 m_endpos->col = startpos->col;
2932
2933 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002934 // can't end before the start
Bram Moolenaar071d4272004-06-13 20:20:40 +00002935 if (end_endpos->lnum == startpos->lnum
2936 && end_endpos->col < startpos->col)
2937 end_endpos->col = startpos->col;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002938 // can't end after the match
Bram Moolenaar071d4272004-06-13 20:20:40 +00002939 limit_pos(end_endpos, m_endpos);
2940
2941 /*
2942 * If the end group is highlighted differently, adjust the pointers.
2943 */
2944 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
2945 {
2946 *end_idx = best_idx;
2947 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
2948 {
2949 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
2950 hl_endpos->col = best_regmatch.endpos[0].col;
2951 }
2952 else
2953 {
2954 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
2955 hl_endpos->col = best_regmatch.startpos[0].col;
2956 }
2957 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
2958
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002959 // can't end before the start
Bram Moolenaar071d4272004-06-13 20:20:40 +00002960 if (hl_endpos->lnum == startpos->lnum
2961 && hl_endpos->col < startpos->col)
2962 hl_endpos->col = startpos->col;
2963 limit_pos(hl_endpos, m_endpos);
2964
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002965 // now the match ends where the highlighting ends, it is turned
2966 // into the matchgroup for the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00002967 *m_endpos = *hl_endpos;
2968 }
2969 else
2970 {
2971 *end_idx = 0;
2972 *hl_endpos = *end_endpos;
2973 }
2974
2975 *flagsp = spp->sp_flags;
2976
2977 had_match = TRUE;
2978 break;
2979 }
2980
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002981 // no match for an END pattern in this line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002982 if (!had_match)
2983 m_endpos->lnum = 0;
2984
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002985 restore_chartab(buf_chartab);
2986
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002987 // Remove external matches.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002988 unref_extmatch(re_extmatch_in);
2989 re_extmatch_in = NULL;
2990}
2991
2992/*
2993 * Limit "pos" not to be after "limit".
2994 */
2995 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002996limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002997{
2998 if (pos->lnum > limit->lnum)
2999 *pos = *limit;
3000 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3001 pos->col = limit->col;
3002}
3003
3004/*
3005 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3006 */
3007 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003008limit_pos_zero(
3009 lpos_T *pos,
3010 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003011{
3012 if (pos->lnum == 0)
3013 *pos = *limit;
3014 else
3015 limit_pos(pos, limit);
3016}
3017
3018/*
3019 * Add offset to matched text for end of match or highlight.
3020 */
3021 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003022syn_add_end_off(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003023 lpos_T *result, // returned position
3024 regmmatch_T *regmatch, // start/end of match
3025 synpat_T *spp, // matched pattern
3026 int idx, // index of offset
3027 int extra) // extra chars for offset to start
Bram Moolenaar071d4272004-06-13 20:20:40 +00003028{
3029 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003030 int off;
3031 char_u *base;
3032 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003033
3034 if (spp->sp_off_flags & (1 << idx))
3035 {
3036 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003037 col = regmatch->startpos[0].col;
3038 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003039 }
3040 else
3041 {
3042 result->lnum = regmatch->endpos[0].lnum;
3043 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003044 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003045 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003046 // Don't go past the end of the line. Matters for "rs=e+2" when there
3047 // is a matchgroup. Watch out for match with last NL in the buffer.
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003048 if (result->lnum > syn_buf->b_ml.ml_line_count)
3049 col = 0;
3050 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003051 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003052 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3053 p = base + col;
3054 if (off > 0)
3055 {
3056 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003057 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003058 }
3059 else if (off < 0)
3060 {
3061 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003062 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003063 }
3064 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003065 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003066 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003067}
3068
3069/*
3070 * Add offset to matched text for start of match or highlight.
3071 * Avoid resulting column to become negative.
3072 */
3073 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003074syn_add_start_off(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003075 lpos_T *result, // returned position
3076 regmmatch_T *regmatch, // start/end of match
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003077 synpat_T *spp,
3078 int idx,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003079 int extra) // extra chars for offset to end
Bram Moolenaar071d4272004-06-13 20:20:40 +00003080{
3081 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003082 int off;
3083 char_u *base;
3084 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003085
3086 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3087 {
3088 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003089 col = regmatch->endpos[0].col;
3090 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003091 }
3092 else
3093 {
3094 result->lnum = regmatch->startpos[0].lnum;
3095 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003096 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003097 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003098 if (result->lnum > syn_buf->b_ml.ml_line_count)
3099 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003100 // a "\n" at the end of the pattern may take us below the last line
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003101 result->lnum = syn_buf->b_ml.ml_line_count;
zeertzjq94b7c322024-03-12 21:50:32 +01003102 col = ml_get_buf_len(syn_buf, result->lnum);
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003103 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003104 if (off != 0)
3105 {
3106 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3107 p = base + col;
3108 if (off > 0)
3109 {
3110 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003111 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003112 }
3113 else if (off < 0)
3114 {
3115 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003116 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003117 }
3118 col = (int)(p - base);
3119 }
3120 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003121}
3122
3123/*
3124 * Get current line in syntax buffer.
3125 */
3126 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003127syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003128{
3129 return ml_get_buf(syn_buf, current_lnum, FALSE);
3130}
3131
3132/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003133 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003134 * Returns TRUE when there is a match.
3135 */
3136 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003137syn_regexec(
3138 regmmatch_T *rmp,
3139 linenr_T lnum,
3140 colnr_T col,
3141 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003142{
Bram Moolenaar6f0cf622022-06-19 12:27:45 +01003143 int r;
3144 int timed_out = FALSE;
Bram Moolenaarf7512552013-06-06 14:55:19 +02003145#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003146 proftime_T pt;
3147
3148 if (syn_time_on)
3149 profile_start(&pt);
3150#endif
3151
Bram Moolenaarbcf94422018-06-23 14:21:42 +02003152 if (rmp->regprog == NULL)
3153 // This can happen if a previous call to vim_regexec_multi() tried to
3154 // use the NFA engine, which resulted in NFA_TOO_EXPENSIVE, and
3155 // compiling the pattern with the other engine fails.
3156 return FALSE;
3157
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003158 rmp->rmm_maxcol = syn_buf->b_p_smc;
Paul Ollis65745772022-06-05 16:55:54 +01003159 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, &timed_out);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003160
Bram Moolenaarf7512552013-06-06 14:55:19 +02003161#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003162 if (syn_time_on)
3163 {
3164 profile_end(&pt);
3165 profile_add(&st->total, &pt);
3166 if (profile_cmp(&pt, &st->slowest) < 0)
3167 st->slowest = pt;
3168 ++st->count;
3169 if (r > 0)
3170 ++st->match;
3171 }
3172#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003173#ifdef FEAT_RELTIME
Bram Moolenaar6f0cf622022-06-19 12:27:45 +01003174 if (timed_out && redrawtime_limit_set && !syn_win->w_s->b_syn_slow)
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003175 {
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003176 syn_win->w_s->b_syn_slow = TRUE;
Bram Moolenaar32526b32019-01-19 17:43:09 +01003177 msg(_("'redrawtime' exceeded, syntax highlighting disabled"));
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003178 }
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003179#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003180
3181 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003182 {
3183 rmp->startpos[0].lnum += lnum;
3184 rmp->endpos[0].lnum += lnum;
3185 return TRUE;
3186 }
3187 return FALSE;
3188}
3189
3190/*
3191 * Check one position in a line for a matching keyword.
3192 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003193 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003194 */
3195 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003196check_keyword_id(
3197 char_u *line,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003198 int startcol, // position in line to check for keyword
3199 int *endcolp, // return: character after found keyword
3200 long *flagsp, // return: flags of matching keyword
3201 short **next_listp, // return: next_list of matching keyword
3202 stateitem_T *cur_si, // item at the top of the stack
3203 int *ccharp UNUSED) // conceal substitution char
Bram Moolenaar071d4272004-06-13 20:20:40 +00003204{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003205 keyentry_T *kp;
3206 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003207 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003208 int kwlen;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003209 char_u keyword[MAXKEYWLEN + 1]; // assume max. keyword len is 80
Bram Moolenaardad6b692005-01-25 22:14:34 +00003210 hashtab_T *ht;
3211 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003212
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003213 // Find first character after the keyword. First character was already
3214 // checked.
Bram Moolenaardad6b692005-01-25 22:14:34 +00003215 kwp = line + startcol;
3216 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003217 do
3218 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003219 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003220 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003221 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00003222 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003223 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003224 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003225
Bram Moolenaardad6b692005-01-25 22:14:34 +00003226 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003227 return 0;
3228
3229 /*
3230 * Must make a copy of the keyword, so we can add a NUL and make it
3231 * lowercase.
3232 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003233 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003234
3235 /*
3236 * Try twice:
3237 * 1. matching case
3238 * 2. ignoring case
3239 */
3240 for (round = 1; round <= 2; ++round)
3241 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003242 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003243 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003244 continue;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003245 if (round == 2) // ignore case
Bram Moolenaardad6b692005-01-25 22:14:34 +00003246 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003247
3248 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003249 * Find keywords that match. There can be several with different
3250 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003251 * When current_next_list is non-zero accept only that group, otherwise:
3252 * Accept a not-contained keyword at toplevel.
3253 * Accept a keyword at other levels only if it is in the contains list.
3254 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003255 hi = hash_find(ht, keyword);
3256 if (!HASHITEM_EMPTY(hi))
3257 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003258 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003259 if (current_next_list != 0
3260 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3261 : (cur_si == NULL
3262 ? !(kp->flags & HL_CONTAINED)
3263 : in_id_list(cur_si, cur_si->si_cont_list,
3264 &kp->k_syn, kp->flags & HL_CONTAINED)))
3265 {
3266 *endcolp = startcol + kwlen;
3267 *flagsp = kp->flags;
3268 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003269#ifdef FEAT_CONCEAL
3270 *ccharp = kp->k_char;
3271#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003272 return kp->k_syn.id;
3273 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003274 }
3275 }
3276 return 0;
3277}
3278
3279/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003280 * Handle ":syntax conceal" command.
3281 */
3282 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003283syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003284{
3285#ifdef FEAT_CONCEAL
3286 char_u *arg = eap->arg;
3287 char_u *next;
3288
3289 eap->nextcmd = find_nextcmd(arg);
3290 if (eap->skip)
3291 return;
3292
3293 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003294 if (*arg == NUL)
3295 {
3296 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003297 msg("syntax conceal on");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003298 else
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003299 msg("syntax conceal off");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003300 }
3301 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003302 curwin->w_s->b_syn_conceal = TRUE;
3303 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3304 curwin->w_s->b_syn_conceal = FALSE;
3305 else
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003306 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003307#endif
3308}
3309
3310/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003311 * Handle ":syntax case" command.
3312 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003313 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003314syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003315{
3316 char_u *arg = eap->arg;
3317 char_u *next;
3318
3319 eap->nextcmd = find_nextcmd(arg);
3320 if (eap->skip)
3321 return;
3322
3323 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003324 if (*arg == NUL)
3325 {
3326 if (curwin->w_s->b_syn_ic)
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003327 msg("syntax case ignore");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003328 else
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003329 msg("syntax case match");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003330 }
3331 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003332 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003333 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003334 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003335 else
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003336 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003337}
3338
3339/*
3340 * Handle ":syntax foldlevel" command.
3341 */
3342 static void
3343syn_cmd_foldlevel(exarg_T *eap, int syncing UNUSED)
3344{
3345 char_u *arg = eap->arg;
3346 char_u *arg_end;
3347
3348 eap->nextcmd = find_nextcmd(arg);
3349 if (eap->skip)
3350 return;
3351
3352 if (*arg == NUL)
3353 {
3354 switch (curwin->w_s->b_syn_foldlevel)
3355 {
Dominique Pellecd53eed2022-02-05 18:53:06 +00003356 case SYNFLD_START: msg("syntax foldlevel start"); break;
3357 case SYNFLD_MINIMUM: msg("syntax foldlevel minimum"); break;
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003358 default: break;
3359 }
3360 return;
3361 }
3362
3363 arg_end = skiptowhite(arg);
3364 if (STRNICMP(arg, "start", 5) == 0 && arg_end - arg == 5)
3365 curwin->w_s->b_syn_foldlevel = SYNFLD_START;
3366 else if (STRNICMP(arg, "minimum", 7) == 0 && arg_end - arg == 7)
3367 curwin->w_s->b_syn_foldlevel = SYNFLD_MINIMUM;
3368 else
3369 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003370 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003371 return;
3372 }
3373
3374 arg = skipwhite(arg_end);
3375 if (*arg != NUL)
3376 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003377 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003378 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003379}
3380
3381/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003382 * Handle ":syntax spell" command.
3383 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003384 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003385syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003386{
3387 char_u *arg = eap->arg;
3388 char_u *next;
3389
3390 eap->nextcmd = find_nextcmd(arg);
3391 if (eap->skip)
3392 return;
3393
3394 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003395 if (*arg == NUL)
3396 {
3397 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
Dominique Pellecd53eed2022-02-05 18:53:06 +00003398 msg("syntax spell toplevel");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003399 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
Dominique Pellecd53eed2022-02-05 18:53:06 +00003400 msg("syntax spell notoplevel");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003401 else
Dominique Pellecd53eed2022-02-05 18:53:06 +00003402 msg("syntax spell default");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003403 }
3404 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003405 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003406 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003407 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003408 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003409 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003410 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003411 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003412 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003413 return;
3414 }
3415
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003416 // assume spell checking changed, force a redraw
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003417 redraw_win_later(curwin, UPD_NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003418}
3419
3420/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003421 * Handle ":syntax iskeyword" command.
3422 */
3423 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003424syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003425{
3426 char_u *arg = eap->arg;
3427 char_u save_chartab[32];
3428 char_u *save_isk;
3429
3430 if (eap->skip)
3431 return;
3432
3433 arg = skipwhite(arg);
3434 if (*arg == NUL)
3435 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003436 msg_puts("\n");
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003437 if (curwin->w_s->b_syn_isk != empty_option)
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003438 {
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003439 msg_puts("syntax iskeyword ");
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003440 msg_outtrans(curwin->w_s->b_syn_isk);
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003441 }
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003442 else
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003443 msg_outtrans((char_u *)_("syntax iskeyword not set"));
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003444 }
3445 else
3446 {
3447 if (STRNICMP(arg, "clear", 5) == 0)
3448 {
3449 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3450 (size_t)32);
3451 clear_string_option(&curwin->w_s->b_syn_isk);
3452 }
3453 else
3454 {
3455 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3456 save_isk = curbuf->b_p_isk;
3457 curbuf->b_p_isk = vim_strsave(arg);
3458
3459 buf_init_chartab(curbuf, FALSE);
3460 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3461 (size_t)32);
3462 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3463 clear_string_option(&curwin->w_s->b_syn_isk);
3464 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3465 curbuf->b_p_isk = save_isk;
3466 }
3467 }
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003468 redraw_win_later(curwin, UPD_NOT_VALID);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003469}
3470
3471/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003472 * Clear all syntax info for one buffer.
3473 */
3474 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003475syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003476{
3477 int i;
3478
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003479 block->b_syn_error = FALSE; // clear previous error
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003480#ifdef FEAT_RELTIME
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003481 block->b_syn_slow = FALSE; // clear previous timeout
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003482#endif
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003483 block->b_syn_ic = FALSE; // Use case, by default
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003484 block->b_syn_foldlevel = SYNFLD_START;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003485 block->b_syn_spell = SYNSPL_DEFAULT; // default spell checking
Bram Moolenaar860cae12010-06-05 23:22:07 +02003486 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003487#ifdef FEAT_CONCEAL
3488 block->b_syn_conceal = FALSE;
3489#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003490
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003491 // free the keywords
Bram Moolenaar860cae12010-06-05 23:22:07 +02003492 clear_keywtab(&block->b_keywtab);
3493 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003494
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003495 // free the syntax patterns
Bram Moolenaar860cae12010-06-05 23:22:07 +02003496 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3497 syn_clear_pattern(block, i);
3498 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003499
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003500 // free the syntax clusters
Bram Moolenaar860cae12010-06-05 23:22:07 +02003501 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3502 syn_clear_cluster(block, i);
3503 ga_clear(&block->b_syn_clusters);
3504 block->b_spell_cluster_id = 0;
3505 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003506
Bram Moolenaar860cae12010-06-05 23:22:07 +02003507 block->b_syn_sync_flags = 0;
3508 block->b_syn_sync_minlines = 0;
3509 block->b_syn_sync_maxlines = 0;
3510 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003511
Bram Moolenaar473de612013-06-08 18:19:48 +02003512 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003513 block->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003514 VIM_CLEAR(block->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003515#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003516 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003517#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003518 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003519
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003520 // free the stored states
Bram Moolenaar860cae12010-06-05 23:22:07 +02003521 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003522 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003523
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003524 // Reset the counter for ":syn include"
Bram Moolenaar42431a72011-04-01 14:44:59 +02003525 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003526}
3527
3528/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003529 * Get rid of ownsyntax for window "wp".
3530 */
3531 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003532reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003533{
3534 if (wp->w_s != &wp->w_buffer->b_s)
3535 {
3536 syntax_clear(wp->w_s);
3537 vim_free(wp->w_s);
3538 wp->w_s = &wp->w_buffer->b_s;
3539 }
3540}
3541
3542/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003543 * Clear syncing info for one buffer.
3544 */
3545 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003546syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003547{
3548 int i;
3549
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003550 // free the syntax patterns
Bram Moolenaar860cae12010-06-05 23:22:07 +02003551 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3552 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3553 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003554
Bram Moolenaar860cae12010-06-05 23:22:07 +02003555 curwin->w_s->b_syn_sync_flags = 0;
3556 curwin->w_s->b_syn_sync_minlines = 0;
3557 curwin->w_s->b_syn_sync_maxlines = 0;
3558 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003559
Bram Moolenaar473de612013-06-08 18:19:48 +02003560 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003561 curwin->w_s->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003562 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003563 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003564
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003565 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003566}
3567
3568/*
3569 * Remove one pattern from the buffer's pattern list.
3570 */
3571 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003572syn_remove_pattern(
3573 synblock_T *block,
3574 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003575{
3576 synpat_T *spp;
3577
Bram Moolenaar860cae12010-06-05 23:22:07 +02003578 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003579#ifdef FEAT_FOLDING
3580 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003581 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003582#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003583 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003584 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003585 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3586 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003587}
3588
3589/*
3590 * Clear and free one syntax pattern. When clearing all, must be called from
3591 * last to first!
3592 */
3593 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003594syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003595{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003596 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003597 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003598 // Only free sp_cont_list and sp_next_list of first start pattern
Bram Moolenaar860cae12010-06-05 23:22:07 +02003599 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003600 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003601 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3602 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3603 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003604 }
3605}
3606
3607/*
3608 * Clear and free one syntax cluster.
3609 */
3610 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003611syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003612{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003613 vim_free(SYN_CLSTR(block)[i].scl_name);
3614 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3615 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003616}
3617
3618/*
3619 * Handle ":syntax clear" command.
3620 */
3621 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003622syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003623{
3624 char_u *arg = eap->arg;
3625 char_u *arg_end;
3626 int id;
3627
3628 eap->nextcmd = find_nextcmd(arg);
3629 if (eap->skip)
3630 return;
3631
3632 /*
3633 * We have to disable this within ":syn include @group filename",
3634 * because otherwise @group would get deleted.
3635 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3636 * clear".
3637 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003638 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003639 return;
3640
Bram Moolenaar1966c242020-04-20 22:42:32 +02003641 if (ends_excmd2(eap->cmd, arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003642 {
3643 /*
3644 * No argument: Clear all syntax items.
3645 */
3646 if (syncing)
3647 syntax_sync_clear();
3648 else
3649 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003650 syntax_clear(curwin->w_s);
3651 if (curwin->w_s == &curwin->w_buffer->b_s)
3652 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003653 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003654 }
3655 }
3656 else
3657 {
3658 /*
3659 * Clear the group IDs that are in the argument.
3660 */
Bram Moolenaar1966c242020-04-20 22:42:32 +02003661 while (!ends_excmd2(eap->cmd, arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003662 {
3663 arg_end = skiptowhite(arg);
3664 if (*arg == '@')
3665 {
3666 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3667 if (id == 0)
3668 {
Bram Moolenaara9fa8c52023-01-02 18:10:04 +00003669 semsg(_(e_no_such_syntax_cluster_str_1), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003670 break;
3671 }
3672 else
3673 {
3674 /*
3675 * We can't physically delete a cluster without changing
3676 * the IDs of other clusters, so we do the next best thing
3677 * and make it empty.
3678 */
3679 short scl_id = id - SYNID_CLUSTER;
3680
Bram Moolenaard23a8232018-02-10 18:45:26 +01003681 VIM_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003682 }
3683 }
3684 else
3685 {
3686 id = syn_namen2id(arg, (int)(arg_end - arg));
3687 if (id == 0)
3688 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +02003689 semsg(_(e_no_such_highlight_group_name_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003690 break;
3691 }
3692 else
3693 syn_clear_one(id, syncing);
3694 }
3695 arg = skipwhite(arg_end);
3696 }
3697 }
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003698 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003699 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003700}
3701
3702/*
3703 * Clear one syntax group for the current buffer.
3704 */
3705 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003706syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003707{
3708 synpat_T *spp;
3709 int idx;
3710
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003711 // Clear keywords only when not ":syn sync clear group-name"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003712 if (!syncing)
3713 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003714 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3715 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003716 }
3717
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003718 // clear the patterns for "id"
Bram Moolenaar860cae12010-06-05 23:22:07 +02003719 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003720 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003721 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003722 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3723 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003724 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003725 }
3726}
3727
3728/*
3729 * Handle ":syntax on" command.
3730 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003731 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003732syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003733{
3734 syn_cmd_onoff(eap, "syntax");
3735}
3736
3737/*
3738 * Handle ":syntax enable" command.
3739 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003740 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003741syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003742{
Bram Moolenaarc8a9fe52021-11-20 19:50:59 +00003743 set_internal_string_var((char_u *)"g:syntax_cmd", (char_u *)"enable");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003744 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003745 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003746}
3747
3748/*
3749 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003750 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003751 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003752 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003753syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003754{
Bram Moolenaar63b91732021-08-05 20:40:03 +02003755 set_nextcmd(eap, eap->arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003756 if (!eap->skip)
3757 {
Bram Moolenaarc8a9fe52021-11-20 19:50:59 +00003758 set_internal_string_var((char_u *)"g:syntax_cmd", (char_u *)"reset");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003759 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003760 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003761 }
3762}
3763
3764/*
3765 * Handle ":syntax manual" command.
3766 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003767 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003768syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003769{
3770 syn_cmd_onoff(eap, "manual");
3771}
3772
3773/*
3774 * Handle ":syntax off" command.
3775 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003776 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003777syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003778{
3779 syn_cmd_onoff(eap, "nosyntax");
3780}
3781
3782 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003783syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003784{
3785 char_u buf[100];
3786
Bram Moolenaar63b91732021-08-05 20:40:03 +02003787 set_nextcmd(eap, eap->arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003788 if (!eap->skip)
3789 {
3790 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003791 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003792 do_cmdline_cmd(buf);
3793 }
3794}
3795
3796/*
3797 * Handle ":syntax [list]" command: list current syntax words.
3798 */
3799 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003800syn_cmd_list(
3801 exarg_T *eap,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003802 int syncing) // when TRUE: list syncing items
Bram Moolenaar071d4272004-06-13 20:20:40 +00003803{
3804 char_u *arg = eap->arg;
3805 int id;
3806 char_u *arg_end;
3807
3808 eap->nextcmd = find_nextcmd(arg);
3809 if (eap->skip)
3810 return;
3811
Bram Moolenaar860cae12010-06-05 23:22:07 +02003812 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003813 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003814 msg(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003815 return;
3816 }
3817
3818 if (syncing)
3819 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003820 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003821 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003822 msg_puts(_("syncing on C-style comments"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003823 syn_lines_msg();
3824 syn_match_msg();
3825 return;
3826 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003827 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003828 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003829 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003830 msg_puts(_("no syncing"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003831 else
3832 {
Bram Moolenaar99502802020-11-18 16:53:23 +01003833 if (curwin->w_s->b_syn_sync_minlines == MAXLNUM)
3834 msg_puts(_("syncing starts at the first line"));
3835 else
3836 {
3837 msg_puts(_("syncing starts "));
3838 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3839 msg_puts(_(" lines before top line"));
3840 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003841 syn_match_msg();
3842 }
3843 return;
3844 }
Bram Moolenaar32526b32019-01-19 17:43:09 +01003845 msg_puts_title(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003846 if (curwin->w_s->b_syn_sync_minlines > 0
3847 || curwin->w_s->b_syn_sync_maxlines > 0
3848 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003849 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003850 msg_puts(_("\nsyncing on items"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003851 syn_lines_msg();
3852 syn_match_msg();
3853 }
3854 }
3855 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01003856 msg_puts_title(_("\n--- Syntax items ---"));
Bram Moolenaar1966c242020-04-20 22:42:32 +02003857 if (ends_excmd2(eap->cmd, arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003858 {
3859 /*
3860 * No argument: List all group IDs and all syntax clusters.
3861 */
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02003862 for (id = 1; id <= highlight_num_groups() && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003863 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003864 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003865 syn_list_cluster(id);
3866 }
3867 else
3868 {
3869 /*
3870 * List the group IDs and syntax clusters that are in the argument.
3871 */
Bram Moolenaar1966c242020-04-20 22:42:32 +02003872 while (!ends_excmd2(eap->cmd, arg) && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003873 {
3874 arg_end = skiptowhite(arg);
3875 if (*arg == '@')
3876 {
3877 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3878 if (id == 0)
Bram Moolenaara9fa8c52023-01-02 18:10:04 +00003879 semsg(_(e_no_such_syntax_cluster_str_2), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003880 else
3881 syn_list_cluster(id - SYNID_CLUSTER);
3882 }
3883 else
3884 {
3885 id = syn_namen2id(arg, (int)(arg_end - arg));
3886 if (id == 0)
Bram Moolenaare29a27f2021-07-20 21:07:36 +02003887 semsg(_(e_no_such_highlight_group_name_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003888 else
3889 syn_list_one(id, syncing, TRUE);
3890 }
3891 arg = skipwhite(arg_end);
3892 }
3893 }
Bram Moolenaar63b91732021-08-05 20:40:03 +02003894 set_nextcmd(eap, arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003895}
3896
3897 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003898syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003899{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003900 if (curwin->w_s->b_syn_sync_maxlines > 0
3901 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003902 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003903 msg_puts("; ");
Bram Moolenaar99502802020-11-18 16:53:23 +01003904 if (curwin->w_s->b_syn_sync_minlines == MAXLNUM)
3905 msg_puts(_("from the first line"));
3906 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003907 {
Bram Moolenaar99502802020-11-18 16:53:23 +01003908 if (curwin->w_s->b_syn_sync_minlines > 0)
3909 {
3910 msg_puts(_("minimal "));
3911 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3912 if (curwin->w_s->b_syn_sync_maxlines)
3913 msg_puts(", ");
3914 }
3915 if (curwin->w_s->b_syn_sync_maxlines > 0)
3916 {
3917 msg_puts(_("maximal "));
3918 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
3919 }
3920 msg_puts(_(" lines before top line"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003921 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003922 }
3923}
3924
3925 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003926syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003927{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003928 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003929 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003930 msg_puts(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003931 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar32526b32019-01-19 17:43:09 +01003932 msg_puts(_(" line breaks"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003933 }
3934}
3935
3936static int last_matchgroup;
3937
3938struct name_list
3939{
3940 int flag;
3941 char *name;
3942};
3943
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01003944static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003945
3946/*
3947 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3948 */
3949 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003950syn_list_one(
3951 int id,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003952 int syncing, // when TRUE: list syncing items
3953 int link_only) // when TRUE; list link-only too
Bram Moolenaar071d4272004-06-13 20:20:40 +00003954{
3955 int attr;
3956 int idx;
3957 int did_header = FALSE;
3958 synpat_T *spp;
3959 static struct name_list namelist1[] =
3960 {
3961 {HL_DISPLAY, "display"},
3962 {HL_CONTAINED, "contained"},
3963 {HL_ONELINE, "oneline"},
3964 {HL_KEEPEND, "keepend"},
3965 {HL_EXTEND, "extend"},
3966 {HL_EXCLUDENL, "excludenl"},
3967 {HL_TRANSP, "transparent"},
3968 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003969#ifdef FEAT_CONCEAL
3970 {HL_CONCEAL, "conceal"},
3971 {HL_CONCEALENDS, "concealends"},
3972#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003973 {0, NULL}
3974 };
3975 static struct name_list namelist2[] =
3976 {
3977 {HL_SKIPWHITE, "skipwhite"},
3978 {HL_SKIPNL, "skipnl"},
3979 {HL_SKIPEMPTY, "skipempty"},
3980 {0, NULL}
3981 };
3982
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003983 attr = HL_ATTR(HLF_D); // highlight like directories
Bram Moolenaar071d4272004-06-13 20:20:40 +00003984
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003985 // list the keywords for "id"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003986 if (!syncing)
3987 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003988 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3989 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003990 did_header, attr);
3991 }
3992
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003993 // list the patterns for "id"
Bram Moolenaar860cae12010-06-05 23:22:07 +02003994 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003995 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003996 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003997 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3998 continue;
3999
4000 (void)syn_list_header(did_header, 999, id);
4001 did_header = TRUE;
4002 last_matchgroup = 0;
4003 if (spp->sp_type == SPTYPE_MATCH)
4004 {
4005 put_pattern("match", ' ', spp, attr);
4006 msg_putchar(' ');
4007 }
4008 else if (spp->sp_type == SPTYPE_START)
4009 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004010 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4011 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4012 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4013 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4014 while (idx < curwin->w_s->b_syn_patterns.ga_len
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004015 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004016 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004017 --idx;
4018 msg_putchar(' ');
4019 }
4020 syn_list_flags(namelist1, spp->sp_flags, attr);
4021
4022 if (spp->sp_cont_list != NULL)
4023 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4024
4025 if (spp->sp_syn.cont_in_list != NULL)
4026 put_id_list((char_u *)"containedin",
4027 spp->sp_syn.cont_in_list, attr);
4028
4029 if (spp->sp_next_list != NULL)
4030 {
4031 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4032 syn_list_flags(namelist2, spp->sp_flags, attr);
4033 }
4034 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4035 {
4036 if (spp->sp_flags & HL_SYNC_HERE)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004037 msg_puts_attr("grouphere", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004038 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004039 msg_puts_attr("groupthere", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004040 msg_putchar(' ');
4041 if (spp->sp_sync_idx >= 0)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004042 msg_outtrans(highlight_group_name(SYN_ITEMS(curwin->w_s)
4043 [spp->sp_sync_idx].sp_syn.id - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004044 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004045 msg_puts("NONE");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004046 msg_putchar(' ');
4047 }
4048 }
4049
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004050 // list the link, if there is one
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004051 if (highlight_link_id(id - 1) && (did_header || link_only) && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004052 {
4053 (void)syn_list_header(did_header, 999, id);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004054 msg_puts_attr("links to", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004055 msg_putchar(' ');
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004056 msg_outtrans(highlight_group_name(highlight_link_id(id - 1) - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004057 }
4058}
4059
4060 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004061syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004062{
4063 int i;
4064
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004065 for (i = 0; nlist[i].flag != 0; ++i)
4066 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004067 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004068 msg_puts_attr(nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004069 msg_putchar(' ');
4070 }
4071}
4072
4073/*
4074 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4075 */
4076 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004077syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004078{
4079 int endcol = 15;
4080
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004081 // slight hack: roughly duplicate the guts of syn_list_header()
Bram Moolenaar071d4272004-06-13 20:20:40 +00004082 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004083 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004084
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004085 if (msg_col >= endcol) // output at least one space
Bram Moolenaar071d4272004-06-13 20:20:40 +00004086 endcol = msg_col + 1;
Christian Brabandt220474d2024-07-20 13:26:44 +02004087 if (Columns <= (long)endcol) // avoid hang for tiny window
4088 endcol = (int)(Columns - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004089
4090 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004091 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004092 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004093 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004094 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004095 }
4096 else
4097 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004098 msg_puts_attr("cluster", HL_ATTR(HLF_D));
4099 msg_puts("=NONE");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004100 }
4101}
4102
4103 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004104put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004105{
4106 short *p;
4107
Bram Moolenaar32526b32019-01-19 17:43:09 +01004108 msg_puts_attr((char *)name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004109 msg_putchar('=');
4110 for (p = list; *p; ++p)
4111 {
4112 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4113 {
4114 if (p[1])
Bram Moolenaar32526b32019-01-19 17:43:09 +01004115 msg_puts("ALLBUT");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004116 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004117 msg_puts("ALL");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004118 }
4119 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4120 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004121 msg_puts("TOP");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004122 }
4123 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4124 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004125 msg_puts("CONTAINED");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004126 }
4127 else if (*p >= SYNID_CLUSTER)
4128 {
4129 short scl_id = *p - SYNID_CLUSTER;
4130
4131 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004132 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004133 }
4134 else
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004135 msg_outtrans(highlight_group_name(*p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004136 if (p[1])
4137 msg_putchar(',');
4138 }
4139 msg_putchar(' ');
4140}
4141
4142 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004143put_pattern(
4144 char *s,
4145 int c,
4146 synpat_T *spp,
4147 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004148{
4149 long n;
4150 int mask;
4151 int first;
4152 static char *sepchars = "/+=-#@\"|'^&";
4153 int i;
4154
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004155 // May have to write "matchgroup=group"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004156 if (last_matchgroup != spp->sp_syn_match_id)
4157 {
4158 last_matchgroup = spp->sp_syn_match_id;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004159 msg_puts_attr("matchgroup", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004160 msg_putchar('=');
4161 if (last_matchgroup == 0)
4162 msg_outtrans((char_u *)"NONE");
4163 else
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004164 msg_outtrans(highlight_group_name(last_matchgroup - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004165 msg_putchar(' ');
4166 }
4167
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004168 // output the name of the pattern and an '=' or ' '
Bram Moolenaar32526b32019-01-19 17:43:09 +01004169 msg_puts_attr(s, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004170 msg_putchar(c);
4171
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004172 // output the pattern, in between a char that is not in the pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00004173 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4174 if (sepchars[++i] == NUL)
4175 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004176 i = 0; // no good char found, just use the first one
Bram Moolenaar071d4272004-06-13 20:20:40 +00004177 break;
4178 }
4179 msg_putchar(sepchars[i]);
4180 msg_outtrans(spp->sp_pattern);
4181 msg_putchar(sepchars[i]);
4182
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004183 // output any pattern options
Bram Moolenaar071d4272004-06-13 20:20:40 +00004184 first = TRUE;
4185 for (i = 0; i < SPO_COUNT; ++i)
4186 {
4187 mask = (1 << i);
4188 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4189 {
4190 if (!first)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004191 msg_putchar(','); // separate with commas
Bram Moolenaar32526b32019-01-19 17:43:09 +01004192 msg_puts(spo_name_tab[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004193 n = spp->sp_offsets[i];
4194 if (i != SPO_LC_OFF)
4195 {
4196 if (spp->sp_off_flags & mask)
4197 msg_putchar('s');
4198 else
4199 msg_putchar('e');
4200 if (n > 0)
4201 msg_putchar('+');
4202 }
4203 if (n || i == SPO_LC_OFF)
4204 msg_outnum(n);
4205 first = FALSE;
4206 }
4207 }
4208 msg_putchar(' ');
4209}
4210
4211/*
4212 * List or clear the keywords for one syntax group.
4213 * Return TRUE if the header has been printed.
4214 */
4215 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004216syn_list_keywords(
4217 int id,
4218 hashtab_T *ht,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004219 int did_header, // header has already been printed
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004220 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004221{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004222 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004223 hashitem_T *hi;
4224 keyentry_T *kp;
4225 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004226 int prev_contained = 0;
4227 short *prev_next_list = NULL;
4228 short *prev_cont_in_list = NULL;
4229 int prev_skipnl = 0;
4230 int prev_skipwhite = 0;
4231 int prev_skipempty = 0;
4232
Bram Moolenaar071d4272004-06-13 20:20:40 +00004233 /*
4234 * Unfortunately, this list of keywords is not sorted on alphabet but on
4235 * hash value...
4236 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004237 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004238 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004239 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004240 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004241 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004242 --todo;
4243 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004244 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004245 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004246 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004247 if (prev_contained != (kp->flags & HL_CONTAINED)
4248 || prev_skipnl != (kp->flags & HL_SKIPNL)
4249 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4250 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4251 || prev_cont_in_list != kp->k_syn.cont_in_list
4252 || prev_next_list != kp->next_list)
4253 outlen = 9999;
4254 else
4255 outlen = (int)STRLEN(kp->keyword);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004256 // output "contained" and "nextgroup" on each line
Bram Moolenaardad6b692005-01-25 22:14:34 +00004257 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004258 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004259 prev_contained = 0;
4260 prev_next_list = NULL;
4261 prev_cont_in_list = NULL;
4262 prev_skipnl = 0;
4263 prev_skipwhite = 0;
4264 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004265 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004266 did_header = TRUE;
4267 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004268 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004269 msg_puts_attr("contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004270 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004271 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004272 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004273 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004274 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004275 put_id_list((char_u *)"containedin",
4276 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004277 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004278 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004279 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004280 if (kp->next_list != prev_next_list)
4281 {
4282 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4283 msg_putchar(' ');
4284 prev_next_list = kp->next_list;
4285 if (kp->flags & HL_SKIPNL)
4286 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004287 msg_puts_attr("skipnl", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004288 msg_putchar(' ');
4289 prev_skipnl = (kp->flags & HL_SKIPNL);
4290 }
4291 if (kp->flags & HL_SKIPWHITE)
4292 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004293 msg_puts_attr("skipwhite", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004294 msg_putchar(' ');
4295 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4296 }
4297 if (kp->flags & HL_SKIPEMPTY)
4298 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004299 msg_puts_attr("skipempty", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004300 msg_putchar(' ');
4301 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4302 }
4303 }
4304 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004305 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004306 }
4307 }
4308 }
4309
4310 return did_header;
4311}
4312
4313 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004314syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004315{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004316 hashitem_T *hi;
4317 keyentry_T *kp;
4318 keyentry_T *kp_prev;
4319 keyentry_T *kp_next;
4320 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004321
Bram Moolenaardad6b692005-01-25 22:14:34 +00004322 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004323 todo = (int)ht->ht_used;
Yegappan Lakshmanan14113fd2023-03-07 17:13:51 +00004324 FOR_ALL_HASHTAB_ITEMS(ht, hi, todo)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004325 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004326 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004327 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004328 --todo;
4329 kp_prev = NULL;
4330 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004331 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004332 if (kp->k_syn.id == id)
4333 {
4334 kp_next = kp->ke_next;
4335 if (kp_prev == NULL)
4336 {
4337 if (kp_next == NULL)
Bram Moolenaaref2c3252022-11-25 16:31:51 +00004338 hash_remove(ht, hi, "syntax clear keyword");
Bram Moolenaardad6b692005-01-25 22:14:34 +00004339 else
4340 hi->hi_key = KE2HIKEY(kp_next);
4341 }
4342 else
4343 kp_prev->ke_next = kp_next;
4344 vim_free(kp->next_list);
4345 vim_free(kp->k_syn.cont_in_list);
4346 vim_free(kp);
4347 kp = kp_next;
4348 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004349 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004350 {
4351 kp_prev = kp;
4352 kp = kp->ke_next;
4353 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004354 }
4355 }
4356 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004357 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004358}
4359
4360/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004361 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004362 */
4363 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004364clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004365{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004366 hashitem_T *hi;
4367 int todo;
4368 keyentry_T *kp;
4369 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004370
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004371 todo = (int)ht->ht_used;
Yegappan Lakshmanan14113fd2023-03-07 17:13:51 +00004372 FOR_ALL_HASHTAB_ITEMS(ht, hi, todo)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004373 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004374 if (!HASHITEM_EMPTY(hi))
4375 {
4376 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004377 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004378 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004379 kp_next = kp->ke_next;
4380 vim_free(kp->next_list);
4381 vim_free(kp->k_syn.cont_in_list);
4382 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004383 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004384 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004385 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004386 hash_clear(ht);
4387 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004388}
4389
4390/*
4391 * Add a keyword to the list of keywords.
4392 */
4393 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004394add_keyword(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004395 char_u *name, // name of keyword
4396 int id, // group ID for this keyword
4397 int flags, // flags for this keyword
4398 short *cont_in_list, // containedin for this keyword
4399 short *next_list, // nextgroup for this keyword
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004400 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004401{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004402 keyentry_T *kp;
4403 hashtab_T *ht;
4404 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004405 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004406 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004407 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004408
Bram Moolenaar860cae12010-06-05 23:22:07 +02004409 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004410 name_ic = str_foldcase(name, (int)STRLEN(name),
4411 name_folded, MAXKEYWLEN + 1);
4412 else
4413 name_ic = name;
Bram Moolenaar47ed5532019-08-08 20:49:14 +02004414 kp = alloc(offsetof(keyentry_T, keyword) + STRLEN(name_ic) + 1);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004415 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004416 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004417 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004418 kp->k_syn.id = id;
4419 kp->k_syn.inc_tag = current_syn_inc_tag;
4420 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004421 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004422 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004423 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004424 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004425 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004426
Bram Moolenaar860cae12010-06-05 23:22:07 +02004427 if (curwin->w_s->b_syn_ic)
4428 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004429 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004430 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004431
Bram Moolenaardad6b692005-01-25 22:14:34 +00004432 hash = hash_hash(kp->keyword);
4433 hi = hash_lookup(ht, kp->keyword, hash);
4434 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004435 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004436 // new keyword, add to hashtable
Bram Moolenaardad6b692005-01-25 22:14:34 +00004437 kp->ke_next = NULL;
4438 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004439 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004440 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004442 // keyword already exists, prepend to list
Bram Moolenaardad6b692005-01-25 22:14:34 +00004443 kp->ke_next = HI2KE(hi);
4444 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004445 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004446}
4447
4448/*
4449 * Get the start and end of the group name argument.
4450 * Return a pointer to the first argument.
4451 * Return NULL if the end of the command was found instead of further args.
4452 */
4453 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004454get_group_name(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004455 char_u *arg, // start of the argument
4456 char_u **name_end) // pointer to end of the name
Bram Moolenaar071d4272004-06-13 20:20:40 +00004457{
4458 char_u *rest;
4459
4460 *name_end = skiptowhite(arg);
4461 rest = skipwhite(*name_end);
4462
4463 /*
4464 * Check if there are enough arguments. The first argument may be a
4465 * pattern, where '|' is allowed, so only check for NUL.
4466 */
4467 if (ends_excmd(*arg) || *rest == NUL)
4468 return NULL;
4469 return rest;
4470}
4471
4472/*
4473 * Check for syntax command option arguments.
4474 * This can be called at any place in the list of arguments, and just picks
4475 * out the arguments that are known. Can be called several times in a row to
4476 * collect all options in between other arguments.
4477 * Return a pointer to the next argument (which isn't an option).
4478 * Return NULL for any error;
4479 */
4480 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004481get_syn_options(
Bram Moolenaar1966c242020-04-20 22:42:32 +02004482 char_u *start, // next argument to be checked
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004483 syn_opt_arg_T *opt, // various things
Bram Moolenaarde318c52017-01-17 16:27:10 +01004484 int *conceal_char UNUSED,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004485 int skip) // TRUE if skipping over command
Bram Moolenaar071d4272004-06-13 20:20:40 +00004486{
Bram Moolenaar1966c242020-04-20 22:42:32 +02004487 char_u *arg = start;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004488 char_u *gname_start, *gname;
4489 int syn_id;
4490 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004491 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004492 int i;
4493 int fidx;
4494 static struct flag
4495 {
4496 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004497 int argtype;
4498 int flags;
4499 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4500 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4501 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4502 {"eExXtTeEnNdD", 0, HL_EXTEND},
4503 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4504 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4505 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4506 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4507 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4508 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4509 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4510 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4511 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004512 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4513 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4514 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004515 {"cCoOnNtTaAiInNsS", 1, 0},
4516 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4517 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004518 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004519 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004520
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004521 if (arg == NULL) // already detected error
Bram Moolenaar071d4272004-06-13 20:20:40 +00004522 return NULL;
4523
Bram Moolenaar860cae12010-06-05 23:22:07 +02004524#ifdef FEAT_CONCEAL
4525 if (curwin->w_s->b_syn_conceal)
4526 opt->flags |= HL_CONCEAL;
4527#endif
4528
Bram Moolenaar071d4272004-06-13 20:20:40 +00004529 for (;;)
4530 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004531 /*
4532 * This is used very often when a large number of keywords is defined.
4533 * Need to skip quickly when no option name is found.
4534 * Also avoid tolower(), it's slow.
4535 */
4536 if (strchr(first_letters, *arg) == NULL)
4537 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004538
K.Takataeeec2542021-06-02 13:28:16 +02004539 for (fidx = ARRAY_LENGTH(flagtab); --fidx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004540 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004541 p = flagtab[fidx].name;
4542 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4543 if (arg[len] != p[i] && arg[len] != p[i + 1])
4544 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004545 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004546 || (flagtab[fidx].argtype > 0
4547 ? arg[len] == '='
Bram Moolenaar1966c242020-04-20 22:42:32 +02004548 : ends_excmd2(start, arg + len))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004549 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004550 if (opt->keyword
4551 && (flagtab[fidx].flags == HL_DISPLAY
4552 || flagtab[fidx].flags == HL_FOLD
4553 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004554 // treat "display", "fold" and "extend" as a keyword
Bram Moolenaar071d4272004-06-13 20:20:40 +00004555 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004556 break;
4557 }
4558 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004559 if (fidx < 0) // no match found
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004560 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004561
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004562 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004563 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004564 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004565 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004566 emsg(_(e_contains_argument_not_accepted_here));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004567 return NULL;
4568 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004569 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004570 return NULL;
4571 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004572 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004573 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004574 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004575 return NULL;
4576 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004577 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004578 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004579 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004580 return NULL;
4581 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004582 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4583 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004584 // cchar=?
Bram Moolenaar860cae12010-06-05 23:22:07 +02004585 if (has_mbyte)
4586 {
Bram Moolenaar264b74f2019-01-24 17:18:42 +01004587#ifdef FEAT_CONCEAL
Bram Moolenaar860cae12010-06-05 23:22:07 +02004588 *conceal_char = mb_ptr2char(arg + 6);
Bram Moolenaar264b74f2019-01-24 17:18:42 +01004589#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004590 arg += mb_ptr2len(arg + 6) - 1;
4591 }
4592 else
Bram Moolenaar56be9502010-06-06 14:20:26 +02004593 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004594#ifdef FEAT_CONCEAL
4595 *conceal_char = arg[6];
4596#else
4597 ;
4598#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004599 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004600#ifdef FEAT_CONCEAL
4601 if (!vim_isprintc_strict(*conceal_char))
4602 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00004603 emsg(_(e_invalid_cchar_value));
Bram Moolenaar4124e722011-01-22 00:58:20 +01004604 return NULL;
4605 }
4606#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004607 arg = skipwhite(arg + 7);
4608 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004609 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004610 {
4611 opt->flags |= flagtab[fidx].flags;
4612 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004613
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004614 if (flagtab[fidx].flags == HL_SYNC_HERE
4615 || flagtab[fidx].flags == HL_SYNC_THERE)
4616 {
4617 if (opt->sync_idx == NULL)
4618 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004619 emsg(_(e_groupthere_not_accepted_here));
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004620 return NULL;
4621 }
4622 gname_start = arg;
4623 arg = skiptowhite(arg);
4624 if (gname_start == arg)
4625 return NULL;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02004626 gname = vim_strnsave(gname_start, arg - gname_start);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004627 if (gname == NULL)
4628 return NULL;
4629 if (STRCMP(gname, "NONE") == 0)
4630 *opt->sync_idx = NONE_IDX;
4631 else
4632 {
4633 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004634 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4635 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
Bram Moolenaar71ccd032020-06-12 22:59:11 +02004636 && SYN_ITEMS(curwin->w_s)[i].sp_type
4637 == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004638 {
4639 *opt->sync_idx = i;
4640 break;
4641 }
4642 if (i < 0)
4643 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004644 semsg(_(e_didnt_find_region_item_for_str), gname);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004645 vim_free(gname);
4646 return NULL;
4647 }
4648 }
4649
4650 vim_free(gname);
4651 arg = skipwhite(arg);
4652 }
4653#ifdef FEAT_FOLDING
4654 else if (flagtab[fidx].flags == HL_FOLD
4655 && foldmethodIsSyntax(curwin))
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004656 // Need to update folds later.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004657 foldUpdateAll(curwin);
4658#endif
4659 }
4660 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004661
4662 return arg;
4663}
4664
4665/*
4666 * Adjustments to syntax item when declared in a ":syn include"'d file.
4667 * Set the contained flag, and if the item is not already contained, add it
4668 * to the specified top-level group, if any.
4669 */
4670 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004671syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004672{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004673 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004674 return;
4675 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004676 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004677 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004678 // We have to alloc this, because syn_combine_list() will free it.
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004679 short *grp_list = ALLOC_MULT(short, 2);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004680 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004681
4682 if (grp_list != NULL)
4683 {
4684 grp_list[0] = id;
4685 grp_list[1] = 0;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004686 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list,
4687 &grp_list, CLUSTER_ADD);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004688 }
4689 }
4690}
4691
4692/*
4693 * Handle ":syntax include [@{group-name}] filename" command.
4694 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004695 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004696syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004697{
4698 char_u *arg = eap->arg;
4699 int sgl_id = 1;
4700 char_u *group_name_end;
4701 char_u *rest;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004702 char *errormsg = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004703 int prev_toplvl_grp;
4704 int prev_syn_inc_tag;
4705 int source = FALSE;
4706
4707 eap->nextcmd = find_nextcmd(arg);
4708 if (eap->skip)
4709 return;
4710
4711 if (arg[0] == '@')
4712 {
4713 ++arg;
4714 rest = get_group_name(arg, &group_name_end);
4715 if (rest == NULL)
4716 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004717 emsg(_(e_filename_required));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004718 return;
4719 }
4720 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004721 if (sgl_id == 0)
4722 return;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004723 // separate_nextcmd() and expand_filename() depend on this
Bram Moolenaar071d4272004-06-13 20:20:40 +00004724 eap->arg = rest;
4725 }
4726
4727 /*
4728 * Everything that's left, up to the next command, should be the
4729 * filename to include.
4730 */
Bram Moolenaar8071cb22019-07-12 17:58:01 +02004731 eap->argt |= (EX_XFILE | EX_NOSPC);
Bram Moolenaarac485062022-03-23 19:45:01 +00004732 separate_nextcmd(eap, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004733 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4734 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004735 // For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4736 // file. Need to expand the file name first. In other cases
4737 // ":runtime!" is used.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004738 source = TRUE;
4739 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4740 {
4741 if (errormsg != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004742 emsg(errormsg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004743 return;
4744 }
4745 }
4746
4747 /*
4748 * Save and restore the existing top-level grouplist id and ":syn
4749 * include" tag around the actual inclusion.
4750 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004751 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4752 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00004753 emsg(_(e_too_many_syntax_includes));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004754 return;
4755 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004756 prev_syn_inc_tag = current_syn_inc_tag;
4757 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004758 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4759 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01004760 if (source ? do_source(eap->arg, FALSE, DOSO_NONE, NULL) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004761 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004762 semsg(_(e_cant_open_file_str), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004763 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004764 current_syn_inc_tag = prev_syn_inc_tag;
4765}
4766
4767/*
4768 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4769 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004770 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004771syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004772{
4773 char_u *arg = eap->arg;
4774 char_u *group_name_end;
4775 int syn_id;
4776 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004777 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004778 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004779 char_u *kw;
4780 syn_opt_arg_T syn_opt_arg;
4781 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004782 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004783
4784 rest = get_group_name(arg, &group_name_end);
4785
4786 if (rest != NULL)
4787 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004788 if (eap->skip)
4789 syn_id = -1;
4790 else
4791 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004792 if (syn_id != 0)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004793 // allocate a buffer, for removing backslashes in the keyword
Bram Moolenaar964b3742019-05-24 18:54:09 +02004794 keyword_copy = alloc(STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004795 if (keyword_copy != NULL)
4796 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004797 syn_opt_arg.flags = 0;
4798 syn_opt_arg.keyword = TRUE;
4799 syn_opt_arg.sync_idx = NULL;
4800 syn_opt_arg.has_cont_list = FALSE;
4801 syn_opt_arg.cont_in_list = NULL;
4802 syn_opt_arg.next_list = NULL;
4803
Bram Moolenaar071d4272004-06-13 20:20:40 +00004804 /*
4805 * The options given apply to ALL keywords, so all options must be
4806 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004807 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004808 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004809 cnt = 0;
4810 p = keyword_copy;
Bram Moolenaar1966c242020-04-20 22:42:32 +02004811 for ( ; rest != NULL && !ends_excmd2(eap->arg, rest);
4812 rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004813 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004814 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4815 eap->skip);
Bram Moolenaar1966c242020-04-20 22:42:32 +02004816 if (rest == NULL || ends_excmd2(eap->arg, rest))
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004817 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004818 // Copy the keyword, removing backslashes, and add a NUL.
Bram Moolenaar1c465442017-03-12 20:10:05 +01004819 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004820 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004821 if (*rest == '\\' && rest[1] != NUL)
4822 ++rest;
4823 *p++ = *rest++;
4824 }
4825 *p++ = NUL;
4826 ++cnt;
4827 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004828
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004829 if (!eap->skip)
4830 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004831 // Adjust flags for use of ":syn include".
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004832 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4833
4834 /*
4835 * 2: Add an entry for each keyword.
4836 */
4837 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4838 {
4839 for (p = vim_strchr(kw, '['); ; )
4840 {
4841 if (p != NULL)
4842 *p = NUL;
4843 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004844 syn_opt_arg.cont_in_list,
4845 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004846 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004847 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004848 if (p[1] == NUL)
4849 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00004850 semsg(_(e_error_missing_rsb_str), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004851 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004852 }
4853 if (p[1] == ']')
4854 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004855 if (p[2] != NUL)
4856 {
Bram Moolenaard82a47d2022-01-05 20:24:39 +00004857 semsg(_(e_trailing_char_after_rsb_str_str),
4858 kw, &p[2]);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004859 goto error;
4860 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004861 kw = p + 1; // skip over the "]"
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004862 break;
4863 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004864 if (has_mbyte)
4865 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004866 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004867
4868 mch_memmove(p, p + 1, l);
4869 p += l;
4870 }
4871 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004872 {
4873 p[0] = p[1];
4874 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004875 }
4876 }
4877 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004878 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004879error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004880 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004881 vim_free(syn_opt_arg.cont_in_list);
4882 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004883 }
4884 }
4885
4886 if (rest != NULL)
Bram Moolenaar63b91732021-08-05 20:40:03 +02004887 set_nextcmd(eap, rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004888 else
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00004889 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004890
Bram Moolenaara4d158b2022-08-14 14:17:45 +01004891 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004892 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004893}
4894
4895/*
4896 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4897 *
4898 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4899 */
4900 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004901syn_cmd_match(
4902 exarg_T *eap,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004903 int syncing) // TRUE for ":syntax sync match .. "
Bram Moolenaar071d4272004-06-13 20:20:40 +00004904{
4905 char_u *arg = eap->arg;
4906 char_u *group_name_end;
4907 char_u *rest;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004908 synpat_T item; // the item found in the line
Bram Moolenaar071d4272004-06-13 20:20:40 +00004909 int syn_id;
4910 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004911 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004912 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004913 int conceal_char = NUL;
Bram Moolenaar1966c242020-04-20 22:42:32 +02004914 int orig_called_emsg = called_emsg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004915
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004916 // Isolate the group name, check for validity
Bram Moolenaar071d4272004-06-13 20:20:40 +00004917 rest = get_group_name(arg, &group_name_end);
4918
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004919 // Get options before the pattern
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004920 syn_opt_arg.flags = 0;
4921 syn_opt_arg.keyword = FALSE;
4922 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4923 syn_opt_arg.has_cont_list = TRUE;
4924 syn_opt_arg.cont_list = NULL;
4925 syn_opt_arg.cont_in_list = NULL;
4926 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01004927 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004928
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004929 // get the pattern.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004930 init_syn_patterns();
Bram Moolenaara80faa82020-04-12 19:37:17 +02004931 CLEAR_FIELD(item);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004932 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004933 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4934 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004935
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004936 // Get options after the pattern
Bram Moolenaarde318c52017-01-17 16:27:10 +01004937 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004938
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004939 if (rest != NULL) // all arguments are valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00004940 {
4941 /*
4942 * Check for trailing command and illegal trailing arguments.
4943 */
Bram Moolenaar63b91732021-08-05 20:40:03 +02004944 set_nextcmd(eap, rest);
Bram Moolenaar1966c242020-04-20 22:42:32 +02004945 if (!ends_excmd2(eap->cmd, rest) || eap->skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004946 rest = NULL;
Yegappan Lakshmananfadc02a2023-01-27 21:03:12 +00004947 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) == OK
Bram Moolenaar071d4272004-06-13 20:20:40 +00004948 && (syn_id = syn_check_group(arg,
4949 (int)(group_name_end - arg))) != 0)
4950 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004951 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004952 /*
4953 * Store the pattern in the syn_items list
4954 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004955 idx = curwin->w_s->b_syn_patterns.ga_len;
4956 SYN_ITEMS(curwin->w_s)[idx] = item;
4957 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4958 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4959 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4960 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4961 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4962 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4963 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4964 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004965 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004966#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02004967 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004968#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004969 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004970 curwin->w_s->b_syn_containedin = TRUE;
4971 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4972 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004973
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004974 // remember that we found a match for syncing on
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004975 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004976 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004977#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004978 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004979 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004980#endif
4981
Bram Moolenaara4d158b2022-08-14 14:17:45 +01004982 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004983 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
4984 return; // don't free the progs and patterns now
Bram Moolenaar071d4272004-06-13 20:20:40 +00004985 }
4986 }
4987
4988 /*
4989 * Something failed, free the allocated memory.
4990 */
Bram Moolenaar473de612013-06-08 18:19:48 +02004991 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004992 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004993 vim_free(syn_opt_arg.cont_list);
4994 vim_free(syn_opt_arg.cont_in_list);
4995 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004996
Bram Moolenaar1966c242020-04-20 22:42:32 +02004997 if (rest == NULL && called_emsg == orig_called_emsg)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00004998 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004999}
5000
5001/*
5002 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5003 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5004 */
5005 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005006syn_cmd_region(
5007 exarg_T *eap,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005008 int syncing) // TRUE for ":syntax sync region .."
Bram Moolenaar071d4272004-06-13 20:20:40 +00005009{
5010 char_u *arg = eap->arg;
5011 char_u *group_name_end;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005012 char_u *rest; // next arg, NULL on error
Bram Moolenaar071d4272004-06-13 20:20:40 +00005013 char_u *key_end;
5014 char_u *key = NULL;
5015 char_u *p;
5016 int item;
5017#define ITEM_START 0
5018#define ITEM_SKIP 1
5019#define ITEM_END 2
5020#define ITEM_MATCHGROUP 3
5021 struct pat_ptr
5022 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005023 synpat_T *pp_synp; // pointer to syn_pattern
5024 int pp_matchgroup_id; // matchgroup ID
5025 struct pat_ptr *pp_next; // pointer to next pat_ptr
Bram Moolenaar071d4272004-06-13 20:20:40 +00005026 } *(pat_ptrs[3]);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005027 // patterns found in the line
Bram Moolenaar071d4272004-06-13 20:20:40 +00005028 struct pat_ptr *ppp;
5029 struct pat_ptr *ppp_next;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005030 int pat_count = 0; // nr of syn_patterns found
Bram Moolenaar071d4272004-06-13 20:20:40 +00005031 int syn_id;
5032 int matchgroup_id = 0;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005033 int not_enough = FALSE; // not enough arguments
5034 int illegal = FALSE; // illegal arguments
Bram Moolenaar071d4272004-06-13 20:20:40 +00005035 int success = FALSE;
5036 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005037 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005038 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005039
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005040 // Isolate the group name, check for validity
Bram Moolenaar071d4272004-06-13 20:20:40 +00005041 rest = get_group_name(arg, &group_name_end);
5042
5043 pat_ptrs[0] = NULL;
5044 pat_ptrs[1] = NULL;
5045 pat_ptrs[2] = NULL;
5046
5047 init_syn_patterns();
5048
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005049 syn_opt_arg.flags = 0;
5050 syn_opt_arg.keyword = FALSE;
5051 syn_opt_arg.sync_idx = NULL;
5052 syn_opt_arg.has_cont_list = TRUE;
5053 syn_opt_arg.cont_list = NULL;
5054 syn_opt_arg.cont_in_list = NULL;
5055 syn_opt_arg.next_list = NULL;
5056
Bram Moolenaar071d4272004-06-13 20:20:40 +00005057 /*
5058 * get the options, patterns and matchgroup.
5059 */
Bram Moolenaar1966c242020-04-20 22:42:32 +02005060 while (rest != NULL && !ends_excmd2(eap->cmd, rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005061 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005062 // Check for option arguments
Bram Moolenaarde318c52017-01-17 16:27:10 +01005063 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar1966c242020-04-20 22:42:32 +02005064 if (rest == NULL || ends_excmd2(eap->cmd, rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005065 break;
5066
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005067 // must be a pattern or matchgroup then
Bram Moolenaar071d4272004-06-13 20:20:40 +00005068 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005069 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005070 ++key_end;
5071 vim_free(key);
Bram Moolenaardf44a272020-06-07 20:49:05 +02005072 key = vim_strnsave_up(rest, key_end - rest);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005073 if (key == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00005074 {
5075 rest = NULL;
5076 break;
5077 }
5078 if (STRCMP(key, "MATCHGROUP") == 0)
5079 item = ITEM_MATCHGROUP;
5080 else if (STRCMP(key, "START") == 0)
5081 item = ITEM_START;
5082 else if (STRCMP(key, "END") == 0)
5083 item = ITEM_END;
5084 else if (STRCMP(key, "SKIP") == 0)
5085 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005086 if (pat_ptrs[ITEM_SKIP] != NULL) // one skip pattern allowed
Bram Moolenaar071d4272004-06-13 20:20:40 +00005087 {
5088 illegal = TRUE;
5089 break;
5090 }
5091 item = ITEM_SKIP;
5092 }
5093 else
5094 break;
5095 rest = skipwhite(key_end);
5096 if (*rest != '=')
5097 {
5098 rest = NULL;
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005099 semsg(_(e_missing_equal_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005100 break;
5101 }
5102 rest = skipwhite(rest + 1);
5103 if (*rest == NUL)
5104 {
5105 not_enough = TRUE;
5106 break;
5107 }
5108
5109 if (item == ITEM_MATCHGROUP)
5110 {
5111 p = skiptowhite(rest);
5112 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5113 matchgroup_id = 0;
5114 else
5115 {
5116 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5117 if (matchgroup_id == 0)
5118 {
5119 illegal = TRUE;
5120 break;
5121 }
5122 }
5123 rest = skipwhite(p);
5124 }
5125 else
5126 {
5127 /*
5128 * Allocate room for a syn_pattern, and link it in the list of
5129 * syn_patterns for this item, at the start (because the list is
5130 * used from end to start).
5131 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005132 ppp = ALLOC_ONE(struct pat_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005133 if (ppp == NULL)
5134 {
5135 rest = NULL;
5136 break;
5137 }
5138 ppp->pp_next = pat_ptrs[item];
5139 pat_ptrs[item] = ppp;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005140 ppp->pp_synp = ALLOC_CLEAR_ONE(synpat_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005141 if (ppp->pp_synp == NULL)
5142 {
5143 rest = NULL;
5144 break;
5145 }
5146
5147 /*
5148 * Get the syntax pattern and the following offset(s).
5149 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005150 // Enable the appropriate \z specials.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005151 if (item == ITEM_START)
5152 reg_do_extmatch = REX_SET;
5153 else if (item == ITEM_SKIP || item == ITEM_END)
5154 reg_do_extmatch = REX_USE;
5155 rest = get_syn_pattern(rest, ppp->pp_synp);
5156 reg_do_extmatch = 0;
5157 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005158 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005159 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5160 ppp->pp_matchgroup_id = matchgroup_id;
5161 ++pat_count;
5162 }
5163 }
5164 vim_free(key);
5165 if (illegal || not_enough)
5166 rest = NULL;
5167
5168 /*
5169 * Must have a "start" and "end" pattern.
5170 */
5171 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5172 pat_ptrs[ITEM_END] == NULL))
5173 {
5174 not_enough = TRUE;
5175 rest = NULL;
5176 }
5177
5178 if (rest != NULL)
5179 {
5180 /*
5181 * Check for trailing garbage or command.
5182 * If OK, add the item.
5183 */
Bram Moolenaar63b91732021-08-05 20:40:03 +02005184 set_nextcmd(eap, rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005185 if (!ends_excmd(*rest) || eap->skip)
5186 rest = NULL;
Yegappan Lakshmananfadc02a2023-01-27 21:03:12 +00005187 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) == OK
Bram Moolenaar071d4272004-06-13 20:20:40 +00005188 && (syn_id = syn_check_group(arg,
5189 (int)(group_name_end - arg))) != 0)
5190 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005191 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005192 /*
5193 * Store the start/skip/end in the syn_items list
5194 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005195 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005196 for (item = ITEM_START; item <= ITEM_END; ++item)
5197 {
5198 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5199 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005200 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5201 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5202 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005203 (item == ITEM_START) ? SPTYPE_START :
5204 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005205 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5206 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005207 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5208 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005209 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005210 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005211#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005212 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005213#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005214 if (item == ITEM_START)
5215 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005216 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005217 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005218 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005219 syn_opt_arg.cont_in_list;
5220 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005221 curwin->w_s->b_syn_containedin = TRUE;
5222 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005223 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005224 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005225 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005226 ++idx;
5227#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005228 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005229 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005230#endif
5231 }
5232 }
5233
Bram Moolenaara4d158b2022-08-14 14:17:45 +01005234 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005235 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
5236 success = TRUE; // don't free the progs and patterns now
Bram Moolenaar071d4272004-06-13 20:20:40 +00005237 }
5238 }
5239
5240 /*
5241 * Free the allocated memory.
5242 */
5243 for (item = ITEM_START; item <= ITEM_END; ++item)
5244 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5245 {
Bram Moolenaar4bbfb0f2019-08-31 15:28:02 +02005246 if (!success && ppp->pp_synp != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005247 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005248 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005249 vim_free(ppp->pp_synp->sp_pattern);
5250 }
5251 vim_free(ppp->pp_synp);
5252 ppp_next = ppp->pp_next;
5253 vim_free(ppp);
5254 }
5255
5256 if (!success)
5257 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005258 vim_free(syn_opt_arg.cont_list);
5259 vim_free(syn_opt_arg.cont_in_list);
5260 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005261 if (not_enough)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005262 semsg(_(e_not_enough_arguments_syntax_region_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005263 else if (illegal || rest == NULL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005264 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005265 }
5266}
5267
5268/*
5269 * A simple syntax group ID comparison function suitable for use in qsort()
5270 */
5271 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005272syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005273{
5274 const short *s1 = v1;
5275 const short *s2 = v2;
5276
5277 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5278}
5279
5280/*
5281 * Combines lists of syntax clusters.
5282 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5283 */
5284 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005285syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005286{
5287 int count1 = 0;
5288 int count2 = 0;
5289 short *g1;
5290 short *g2;
5291 short *clstr = NULL;
5292 int count;
5293 int round;
5294
5295 /*
5296 * Handle degenerate cases.
5297 */
5298 if (*clstr2 == NULL)
5299 return;
5300 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5301 {
5302 if (list_op == CLUSTER_REPLACE)
5303 vim_free(*clstr1);
5304 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5305 *clstr1 = *clstr2;
5306 else
5307 vim_free(*clstr2);
5308 return;
5309 }
5310
5311 for (g1 = *clstr1; *g1; g1++)
5312 ++count1;
5313 for (g2 = *clstr2; *g2; g2++)
5314 ++count2;
5315
5316 /*
5317 * For speed purposes, sort both lists.
5318 */
5319 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5320 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5321
5322 /*
5323 * We proceed in two passes; in round 1, we count the elements to place
5324 * in the new list, and in round 2, we allocate and populate the new
5325 * list. For speed, we use a mergesort-like method, adding the smaller
5326 * of the current elements in each list to the new list.
5327 */
5328 for (round = 1; round <= 2; round++)
5329 {
5330 g1 = *clstr1;
5331 g2 = *clstr2;
5332 count = 0;
5333
5334 /*
5335 * First, loop through the lists until one of them is empty.
5336 */
5337 while (*g1 && *g2)
5338 {
5339 /*
5340 * We always want to add from the first list.
5341 */
5342 if (*g1 < *g2)
5343 {
5344 if (round == 2)
5345 clstr[count] = *g1;
5346 count++;
5347 g1++;
5348 continue;
5349 }
5350 /*
5351 * We only want to add from the second list if we're adding the
5352 * lists.
5353 */
5354 if (list_op == CLUSTER_ADD)
5355 {
5356 if (round == 2)
5357 clstr[count] = *g2;
5358 count++;
5359 }
5360 if (*g1 == *g2)
5361 g1++;
5362 g2++;
5363 }
5364
5365 /*
5366 * Now add the leftovers from whichever list didn't get finished
5367 * first. As before, we only want to add from the second list if
5368 * we're adding the lists.
5369 */
5370 for (; *g1; g1++, count++)
5371 if (round == 2)
5372 clstr[count] = *g1;
5373 if (list_op == CLUSTER_ADD)
5374 for (; *g2; g2++, count++)
5375 if (round == 2)
5376 clstr[count] = *g2;
5377
5378 if (round == 1)
5379 {
5380 /*
5381 * If the group ended up empty, we don't need to allocate any
5382 * space for it.
5383 */
5384 if (count == 0)
5385 {
5386 clstr = NULL;
5387 break;
5388 }
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005389 clstr = ALLOC_MULT(short, count + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005390 if (clstr == NULL)
5391 break;
5392 clstr[count] = 0;
5393 }
5394 }
5395
5396 /*
5397 * Finally, put the new list in place.
5398 */
5399 vim_free(*clstr1);
5400 vim_free(*clstr2);
5401 *clstr1 = clstr;
5402}
5403
5404/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005405 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005406 * If it is not found, 0 is returned.
5407 */
5408 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005409syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005410{
5411 int i;
5412 char_u *name_u;
5413
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005414 // Avoid using stricmp() too much, it's slow on some systems
Bram Moolenaar071d4272004-06-13 20:20:40 +00005415 name_u = vim_strsave_up(name);
5416 if (name_u == NULL)
5417 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005418 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5419 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5420 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005421 break;
5422 vim_free(name_u);
5423 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5424}
5425
5426/*
5427 * Like syn_scl_name2id(), but take a pointer + length argument.
5428 */
5429 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005430syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005431{
5432 char_u *name;
5433 int id = 0;
5434
5435 name = vim_strnsave(linep, len);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00005436 if (name == NULL)
5437 return 0;
5438
5439 id = syn_scl_name2id(name);
5440 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005441 return id;
5442}
5443
5444/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005445 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005446 * The argument is a pointer to the name and the length of the name.
5447 * If it doesn't exist yet, a new entry is created.
5448 * Return 0 for failure.
5449 */
5450 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005451syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005452{
5453 int id;
5454 char_u *name;
5455
5456 name = vim_strnsave(pp, len);
5457 if (name == NULL)
5458 return 0;
5459
5460 id = syn_scl_name2id(name);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005461 if (id == 0) // doesn't exist yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00005462 id = syn_add_cluster(name);
5463 else
5464 vim_free(name);
5465 return id;
5466}
5467
5468/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005469 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005470 * "name" must be an allocated string, it will be consumed.
5471 * Return 0 for failure.
5472 */
5473 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005474syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005475{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005476 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005477
5478 /*
5479 * First call for this growarray: init growing array.
5480 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005481 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005482 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005483 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5484 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005485 }
5486
Bram Moolenaar42431a72011-04-01 14:44:59 +02005487 len = curwin->w_s->b_syn_clusters.ga_len;
5488 if (len >= MAX_CLUSTER_ID)
5489 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00005490 emsg(_(e_too_many_syntax_clusters));
Bram Moolenaar42431a72011-04-01 14:44:59 +02005491 vim_free(name);
5492 return 0;
5493 }
5494
Bram Moolenaar071d4272004-06-13 20:20:40 +00005495 /*
5496 * Make room for at least one other cluster entry.
5497 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005498 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005499 {
5500 vim_free(name);
5501 return 0;
5502 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005503
Bram Moolenaara80faa82020-04-12 19:37:17 +02005504 CLEAR_POINTER(&(SYN_CLSTR(curwin->w_s)[len]));
Bram Moolenaar860cae12010-06-05 23:22:07 +02005505 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5506 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5507 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5508 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005509
Bram Moolenaar217ad922005-03-20 22:37:15 +00005510 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005511 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005512 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005513 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005514
Bram Moolenaar071d4272004-06-13 20:20:40 +00005515 return len + SYNID_CLUSTER;
5516}
5517
5518/*
5519 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5520 * [add={groupname},..] [remove={groupname},..]".
5521 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005522 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005523syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005524{
5525 char_u *arg = eap->arg;
5526 char_u *group_name_end;
5527 char_u *rest;
5528 int scl_id;
5529 short *clstr_list;
5530 int got_clstr = FALSE;
5531 int opt_len;
5532 int list_op;
5533
5534 eap->nextcmd = find_nextcmd(arg);
5535 if (eap->skip)
5536 return;
5537
5538 rest = get_group_name(arg, &group_name_end);
5539
5540 if (rest != NULL)
5541 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005542 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5543 if (scl_id == 0)
5544 return;
5545 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005546
5547 for (;;)
5548 {
5549 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005550 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005551 {
5552 opt_len = 3;
5553 list_op = CLUSTER_ADD;
5554 }
5555 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005556 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005557 {
5558 opt_len = 6;
5559 list_op = CLUSTER_SUBTRACT;
5560 }
5561 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005562 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005563 {
5564 opt_len = 8;
5565 list_op = CLUSTER_REPLACE;
5566 }
5567 else
5568 break;
5569
5570 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005571 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005572 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005573 semsg(_(e_invalid_argument_str), rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005574 break;
5575 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005576 if (scl_id >= 0)
5577 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005578 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005579 else
5580 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005581 got_clstr = TRUE;
5582 }
5583
5584 if (got_clstr)
5585 {
Bram Moolenaara4d158b2022-08-14 14:17:45 +01005586 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005587 syn_stack_free_all(curwin->w_s); // Need to recompute all.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005588 }
5589 }
5590
5591 if (!got_clstr)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005592 emsg(_(e_no_cluster_specified));
Bram Moolenaar1966c242020-04-20 22:42:32 +02005593 if (rest == NULL || !ends_excmd2(eap->cmd, rest))
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005594 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005595}
5596
5597/*
5598 * On first call for current buffer: Init growing array.
5599 */
5600 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005601init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005602{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005603 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5604 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005605}
5606
5607/*
5608 * Get one pattern for a ":syntax match" or ":syntax region" command.
5609 * Stores the pattern and program in a synpat_T.
5610 * Returns a pointer to the next argument, or NULL in case of an error.
5611 */
5612 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005613get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005614{
5615 char_u *end;
5616 int *p;
5617 int idx;
5618 char_u *cpo_save;
5619
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005620 // need at least three chars
Bram Moolenaar38219782015-08-11 15:27:13 +02005621 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005622 return NULL;
5623
Bram Moolenaare8c4abb2020-04-02 21:13:25 +02005624 end = skip_regexp(arg + 1, *arg, TRUE);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005625 if (*end != *arg) // end delimiter not found
Bram Moolenaar071d4272004-06-13 20:20:40 +00005626 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005627 semsg(_(e_pattern_delimiter_not_found_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005628 return NULL;
5629 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005630 // store the pattern and compiled regexp program
Bram Moolenaar71ccd032020-06-12 22:59:11 +02005631 if ((ci->sp_pattern = vim_strnsave(arg + 1, end - arg - 1)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005632 return NULL;
5633
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005634 // Make 'cpoptions' empty, to avoid the 'l' flag
Bram Moolenaar071d4272004-06-13 20:20:40 +00005635 cpo_save = p_cpo;
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01005636 p_cpo = empty_option;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005637 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5638 p_cpo = cpo_save;
5639
5640 if (ci->sp_prog == NULL)
5641 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005642 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005643#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005644 syn_clear_time(&ci->sp_time);
5645#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005646
5647 /*
5648 * Check for a match, highlight or region offset.
5649 */
5650 ++end;
5651 do
5652 {
5653 for (idx = SPO_COUNT; --idx >= 0; )
5654 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5655 break;
5656 if (idx >= 0)
5657 {
5658 p = &(ci->sp_offsets[idx]);
5659 if (idx != SPO_LC_OFF)
5660 switch (end[3])
5661 {
5662 case 's': break;
5663 case 'b': break;
5664 case 'e': idx += SPO_COUNT; break;
5665 default: idx = -1; break;
5666 }
5667 if (idx >= 0)
5668 {
5669 ci->sp_off_flags |= (1 << idx);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005670 if (idx == SPO_LC_OFF) // lc=99
Bram Moolenaar071d4272004-06-13 20:20:40 +00005671 {
5672 end += 3;
5673 *p = getdigits(&end);
5674
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005675 // "lc=" offset automatically sets "ms=" offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00005676 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5677 {
5678 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5679 ci->sp_offsets[SPO_MS_OFF] = *p;
5680 }
5681 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005682 else // yy=x+99
Bram Moolenaar071d4272004-06-13 20:20:40 +00005683 {
5684 end += 4;
5685 if (*end == '+')
5686 {
5687 ++end;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005688 *p = getdigits(&end); // positive offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00005689 }
5690 else if (*end == '-')
5691 {
5692 ++end;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005693 *p = -getdigits(&end); // negative offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00005694 }
5695 }
5696 if (*end != ',')
5697 break;
5698 ++end;
5699 }
5700 }
5701 } while (idx >= 0);
5702
Bram Moolenaar1966c242020-04-20 22:42:32 +02005703 if (!ends_excmd2(arg, end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005704 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005705 semsg(_(e_garbage_after_pattern_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005706 return NULL;
5707 }
5708 return skipwhite(end);
5709}
5710
5711/*
5712 * Handle ":syntax sync .." command.
5713 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005714 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005715syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005716{
5717 char_u *arg_start = eap->arg;
5718 char_u *arg_end;
5719 char_u *key = NULL;
5720 char_u *next_arg;
5721 int illegal = FALSE;
5722 int finished = FALSE;
5723 long n;
5724 char_u *cpo_save;
5725
Bram Moolenaar1966c242020-04-20 22:42:32 +02005726 if (ends_excmd2(eap->cmd, arg_start))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005727 {
5728 syn_cmd_list(eap, TRUE);
5729 return;
5730 }
5731
Bram Moolenaar1966c242020-04-20 22:42:32 +02005732 while (!ends_excmd2(eap->cmd, arg_start))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005733 {
5734 arg_end = skiptowhite(arg_start);
5735 next_arg = skipwhite(arg_end);
5736 vim_free(key);
Bram Moolenaardf44a272020-06-07 20:49:05 +02005737 key = vim_strnsave_up(arg_start, arg_end - arg_start);
Bram Moolenaar58bb61c2020-07-10 20:30:12 +02005738 if (key == NULL)
5739 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005740 if (STRCMP(key, "CCOMMENT") == 0)
5741 {
5742 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005743 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar1966c242020-04-20 22:42:32 +02005744 if (!ends_excmd2(eap->cmd, next_arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005745 {
5746 arg_end = skiptowhite(next_arg);
5747 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005748 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005749 (int)(arg_end - next_arg));
5750 next_arg = skipwhite(arg_end);
5751 }
5752 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005753 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005754 }
5755 else if ( STRNCMP(key, "LINES", 5) == 0
5756 || STRNCMP(key, "MINLINES", 8) == 0
5757 || STRNCMP(key, "MAXLINES", 8) == 0
5758 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5759 {
5760 if (key[4] == 'S')
5761 arg_end = key + 6;
5762 else if (key[0] == 'L')
5763 arg_end = key + 11;
5764 else
5765 arg_end = key + 9;
5766 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5767 {
5768 illegal = TRUE;
5769 break;
5770 }
5771 n = getdigits(&arg_end);
5772 if (!eap->skip)
5773 {
5774 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005775 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005776 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005777 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005778 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005779 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005780 }
5781 }
5782 else if (STRCMP(key, "FROMSTART") == 0)
5783 {
5784 if (!eap->skip)
5785 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005786 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5787 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005788 }
5789 }
5790 else if (STRCMP(key, "LINECONT") == 0)
5791 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005792 if (*next_arg == NUL) // missing pattern
Bram Moolenaar2795e212016-01-05 22:04:49 +01005793 {
5794 illegal = TRUE;
5795 break;
5796 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005797 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005798 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005799 emsg(_(e_syntax_sync_line_continuations_pattern_specified_twice));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005800 finished = TRUE;
5801 break;
5802 }
Bram Moolenaare8c4abb2020-04-02 21:13:25 +02005803 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005804 if (*arg_end != *next_arg) // end delimiter not found
Bram Moolenaar071d4272004-06-13 20:20:40 +00005805 {
5806 illegal = TRUE;
5807 break;
5808 }
5809
5810 if (!eap->skip)
5811 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005812 // store the pattern and compiled regexp program
Bram Moolenaar71ccd032020-06-12 22:59:11 +02005813 if ((curwin->w_s->b_syn_linecont_pat =
5814 vim_strnsave(next_arg + 1,
5815 arg_end - next_arg - 1)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005816 {
5817 finished = TRUE;
5818 break;
5819 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005820 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005821
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005822 // Make 'cpoptions' empty, to avoid the 'l' flag
Bram Moolenaar071d4272004-06-13 20:20:40 +00005823 cpo_save = p_cpo;
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01005824 p_cpo = empty_option;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005825 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005826 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005827 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005828#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005829 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5830#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005831
Bram Moolenaar860cae12010-06-05 23:22:07 +02005832 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005833 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01005834 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005835 finished = TRUE;
5836 break;
5837 }
5838 }
5839 next_arg = skipwhite(arg_end + 1);
5840 }
5841 else
5842 {
5843 eap->arg = next_arg;
5844 if (STRCMP(key, "MATCH") == 0)
5845 syn_cmd_match(eap, TRUE);
5846 else if (STRCMP(key, "REGION") == 0)
5847 syn_cmd_region(eap, TRUE);
5848 else if (STRCMP(key, "CLEAR") == 0)
5849 syn_cmd_clear(eap, TRUE);
5850 else
5851 illegal = TRUE;
5852 finished = TRUE;
5853 break;
5854 }
5855 arg_start = next_arg;
5856 }
5857 vim_free(key);
5858 if (illegal)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005859 semsg(_(e_illegal_arguments_str), arg_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005860 else if (!finished)
5861 {
Bram Moolenaar63b91732021-08-05 20:40:03 +02005862 set_nextcmd(eap, arg_start);
Bram Moolenaara4d158b2022-08-14 14:17:45 +01005863 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005864 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005865 }
5866}
5867
5868/*
5869 * Convert a line of highlight group names into a list of group ID numbers.
5870 * "arg" should point to the "contains" or "nextgroup" keyword.
5871 * "arg" is advanced to after the last group name.
5872 * Careful: the argument is modified (NULs added).
5873 * returns FAIL for some error, OK for success.
5874 */
5875 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005876get_id_list(
5877 char_u **arg,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005878 int keylen, // length of keyword
5879 short **list, // where to store the resulting list, if not
5880 // NULL, the list is silently skipped!
Bram Moolenaarde318c52017-01-17 16:27:10 +01005881 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005882{
5883 char_u *p = NULL;
5884 char_u *end;
5885 int round;
5886 int count;
5887 int total_count = 0;
5888 short *retval = NULL;
5889 char_u *name;
5890 regmatch_T regmatch;
5891 int id;
5892 int i;
5893 int failed = FALSE;
5894
5895 /*
5896 * We parse the list twice:
5897 * round == 1: count the number of items, allocate the array.
5898 * round == 2: fill the array with the items.
5899 * In round 1 new groups may be added, causing the number of items to
5900 * grow when a regexp is used. In that case round 1 is done once again.
5901 */
5902 for (round = 1; round <= 2; ++round)
5903 {
5904 /*
5905 * skip "contains"
5906 */
5907 p = skipwhite(*arg + keylen);
5908 if (*p != '=')
5909 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005910 semsg(_(e_missing_equal_sign_str), *arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005911 break;
5912 }
5913 p = skipwhite(p + 1);
Bram Moolenaar1966c242020-04-20 22:42:32 +02005914 if (ends_excmd2(*arg, p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005915 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005916 semsg(_(e_empty_argument_str), *arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005917 break;
5918 }
5919
5920 /*
5921 * parse the arguments after "contains"
5922 */
5923 count = 0;
Bram Moolenaar1966c242020-04-20 22:42:32 +02005924 while (!ends_excmd2(*arg, p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005925 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01005926 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005927 ;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005928 name = alloc(end - p + 3); // leave room for "^$"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005929 if (name == NULL)
5930 {
5931 failed = TRUE;
5932 break;
5933 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005934 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005935 if ( STRCMP(name + 1, "ALLBUT") == 0
5936 || STRCMP(name + 1, "ALL") == 0
5937 || STRCMP(name + 1, "TOP") == 0
5938 || STRCMP(name + 1, "CONTAINED") == 0)
5939 {
5940 if (TOUPPER_ASC(**arg) != 'C')
5941 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005942 semsg(_(e_str_not_allowed_here), name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005943 failed = TRUE;
5944 vim_free(name);
5945 break;
5946 }
5947 if (count != 0)
5948 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005949 semsg(_(e_str_must_be_first_in_contains_list), name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005950 failed = TRUE;
5951 vim_free(name);
5952 break;
5953 }
5954 if (name[1] == 'A')
Bram Moolenaar2e240bd2021-04-14 11:15:08 +02005955 id = SYNID_ALLBUT + current_syn_inc_tag;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005956 else if (name[1] == 'T')
Bram Moolenaar2e240bd2021-04-14 11:15:08 +02005957 {
5958 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
5959 id = curwin->w_s->b_syn_topgrp;
5960 else
5961 id = SYNID_TOP + current_syn_inc_tag;
5962 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005963 else
Bram Moolenaar2e240bd2021-04-14 11:15:08 +02005964 id = SYNID_CONTAINED + current_syn_inc_tag;
5965
Bram Moolenaar071d4272004-06-13 20:20:40 +00005966 }
5967 else if (name[1] == '@')
5968 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01005969 if (skip)
5970 id = -1;
5971 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01005972 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005973 }
5974 else
5975 {
5976 /*
5977 * Handle full group name.
5978 */
5979 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5980 id = syn_check_group(name + 1, (int)(end - p));
5981 else
5982 {
5983 /*
5984 * Handle match of regexp with group names.
5985 */
5986 *name = '^';
5987 STRCAT(name, "$");
5988 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5989 if (regmatch.regprog == NULL)
5990 {
5991 failed = TRUE;
5992 vim_free(name);
5993 break;
5994 }
5995
5996 regmatch.rm_ic = TRUE;
5997 id = 0;
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02005998 for (i = highlight_num_groups(); --i >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00005999 {
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02006000 if (vim_regexec(&regmatch, highlight_group_name(i),
Bram Moolenaar071d4272004-06-13 20:20:40 +00006001 (colnr_T)0))
6002 {
6003 if (round == 2)
6004 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006005 // Got more items than expected; can happen
6006 // when adding items that match:
6007 // "contains=a.*b,axb".
6008 // Go back to first round
Bram Moolenaar071d4272004-06-13 20:20:40 +00006009 if (count >= total_count)
6010 {
6011 vim_free(retval);
6012 round = 1;
6013 }
6014 else
6015 retval[count] = i + 1;
6016 }
6017 ++count;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006018 id = -1; // remember that we found one
Bram Moolenaar071d4272004-06-13 20:20:40 +00006019 }
6020 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006021 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006022 }
6023 }
6024 vim_free(name);
6025 if (id == 0)
6026 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00006027 semsg(_(e_unknown_group_name_str), p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006028 failed = TRUE;
6029 break;
6030 }
6031 if (id > 0)
6032 {
6033 if (round == 2)
6034 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006035 // Got more items than expected, go back to first round
Bram Moolenaar071d4272004-06-13 20:20:40 +00006036 if (count >= total_count)
6037 {
6038 vim_free(retval);
6039 round = 1;
6040 }
6041 else
6042 retval[count] = id;
6043 }
6044 ++count;
6045 }
6046 p = skipwhite(end);
6047 if (*p != ',')
6048 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006049 p = skipwhite(p + 1); // skip comma in between arguments
Bram Moolenaar071d4272004-06-13 20:20:40 +00006050 }
6051 if (failed)
6052 break;
6053 if (round == 1)
6054 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006055 retval = ALLOC_MULT(short, count + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006056 if (retval == NULL)
6057 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006058 retval[count] = 0; // zero means end of the list
Bram Moolenaar071d4272004-06-13 20:20:40 +00006059 total_count = count;
6060 }
6061 }
6062
6063 *arg = p;
6064 if (failed || retval == NULL)
6065 {
6066 vim_free(retval);
6067 return FAIL;
6068 }
6069
6070 if (*list == NULL)
6071 *list = retval;
6072 else
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006073 vim_free(retval); // list already found, don't overwrite it
Bram Moolenaar071d4272004-06-13 20:20:40 +00006074
6075 return OK;
6076}
6077
6078/*
6079 * Make a copy of an ID list.
6080 */
6081 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006082copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006083{
6084 int len;
6085 int count;
6086 short *retval;
6087
6088 if (list == NULL)
6089 return NULL;
6090
6091 for (count = 0; list[count]; ++count)
6092 ;
6093 len = (count + 1) * sizeof(short);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006094 retval = alloc(len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006095 if (retval != NULL)
6096 mch_memmove(retval, list, (size_t)len);
6097
6098 return retval;
6099}
6100
6101/*
6102 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6103 * "cur_si" can be NULL if not checking the "containedin" list.
6104 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6105 * the current item.
6106 * This function is called very often, keep it fast!!
6107 */
6108 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006109in_id_list(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006110 stateitem_T *cur_si, // current item or NULL
6111 short *list, // id list
6112 struct sp_syn *ssp, // group id and ":syn include" tag of group
6113 int contained) // group id is contained
Bram Moolenaar071d4272004-06-13 20:20:40 +00006114{
6115 int retval;
6116 short *scl_list;
6117 short item;
6118 short id = ssp->id;
6119 static int depth = 0;
6120 int r;
6121
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006122 // If ssp has a "containedin" list and "cur_si" is in it, return TRUE.
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006123 if (cur_si != NULL && ssp->cont_in_list != NULL
6124 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006125 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006126 // Ignore transparent items without a contains argument. Double check
6127 // that we don't go back past the first one.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006128 while ((cur_si->si_flags & HL_TRANS_CONT)
6129 && cur_si > (stateitem_T *)(current_state.ga_data))
6130 --cur_si;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006131 // cur_si->si_idx is -1 for keywords, these never contain anything.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006132 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006133 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6134 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006135 return TRUE;
6136 }
6137
6138 if (list == NULL)
6139 return FALSE;
6140
6141 /*
6142 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6143 * inside anything. Only allow not-contained groups.
6144 */
6145 if (list == ID_LIST_ALL)
6146 return !contained;
6147
6148 /*
6149 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6150 * contains list. We also require that "id" is at the same ":syn include"
6151 * level as the list.
6152 */
6153 item = *list;
6154 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6155 {
6156 if (item < SYNID_TOP)
6157 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006158 // ALL or ALLBUT: accept all groups in the same file
Bram Moolenaar071d4272004-06-13 20:20:40 +00006159 if (item - SYNID_ALLBUT != ssp->inc_tag)
6160 return FALSE;
6161 }
6162 else if (item < SYNID_CONTAINED)
6163 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006164 // TOP: accept all not-contained groups in the same file
Bram Moolenaar071d4272004-06-13 20:20:40 +00006165 if (item - SYNID_TOP != ssp->inc_tag || contained)
6166 return FALSE;
6167 }
6168 else
6169 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006170 // CONTAINED: accept all contained groups in the same file
Bram Moolenaar071d4272004-06-13 20:20:40 +00006171 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6172 return FALSE;
6173 }
6174 item = *++list;
6175 retval = FALSE;
6176 }
6177 else
6178 retval = TRUE;
6179
6180 /*
6181 * Return "retval" if id is in the contains list.
6182 */
6183 while (item != 0)
6184 {
6185 if (item == id)
6186 return retval;
6187 if (item >= SYNID_CLUSTER)
6188 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006189 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006190 // restrict recursiveness to 30 to avoid an endless loop for a
6191 // cluster that includes itself (indirectly)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006192 if (scl_list != NULL && depth < 30)
6193 {
6194 ++depth;
6195 r = in_id_list(NULL, scl_list, ssp, contained);
6196 --depth;
6197 if (r)
6198 return retval;
6199 }
6200 }
6201 item = *++list;
6202 }
6203 return !retval;
6204}
6205
6206struct subcommand
6207{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006208 char *name; // subcommand name
6209 void (*func)(exarg_T *, int); // function to call
Bram Moolenaar071d4272004-06-13 20:20:40 +00006210};
6211
6212static struct subcommand subcommands[] =
6213{
6214 {"case", syn_cmd_case},
6215 {"clear", syn_cmd_clear},
6216 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006217 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006218 {"enable", syn_cmd_enable},
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006219 {"foldlevel", syn_cmd_foldlevel},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006220 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006221 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006222 {"keyword", syn_cmd_keyword},
6223 {"list", syn_cmd_list},
6224 {"manual", syn_cmd_manual},
6225 {"match", syn_cmd_match},
6226 {"on", syn_cmd_on},
6227 {"off", syn_cmd_off},
6228 {"region", syn_cmd_region},
6229 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006230 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006231 {"sync", syn_cmd_sync},
6232 {"", syn_cmd_list},
6233 {NULL, NULL}
6234};
6235
6236/*
6237 * ":syntax".
6238 * This searches the subcommands[] table for the subcommand name, and calls a
6239 * syntax_subcommand() function to do the rest.
6240 */
6241 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006242ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006243{
6244 char_u *arg = eap->arg;
6245 char_u *subcmd_end;
6246 char_u *subcmd_name;
6247 int i;
6248
6249 syn_cmdlinep = eap->cmdlinep;
6250
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006251 // isolate subcommand name
Bram Moolenaar071d4272004-06-13 20:20:40 +00006252 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6253 ;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02006254 subcmd_name = vim_strnsave(arg, subcmd_end - arg);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00006255 if (subcmd_name == NULL)
6256 return;
6257
6258 if (eap->skip) // skip error messages for all subcommands
6259 ++emsg_skip;
6260 for (i = 0; ; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006261 {
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00006262 if (subcommands[i].name == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006263 {
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00006264 semsg(_(e_invalid_syntax_subcommand_str), subcmd_name);
6265 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006266 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00006267 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6268 {
6269 eap->arg = skipwhite(subcmd_end);
6270 (subcommands[i].func)(eap, FALSE);
6271 break;
6272 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006273 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00006274 vim_free(subcmd_name);
6275 if (eap->skip)
6276 --emsg_skip;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006277}
6278
Bram Moolenaar860cae12010-06-05 23:22:07 +02006279 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006280ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006281{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006282 char_u *old_value;
6283 char_u *new_value;
6284
Bram Moolenaar860cae12010-06-05 23:22:07 +02006285 if (curwin->w_s == &curwin->w_buffer->b_s)
6286 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006287 curwin->w_s = ALLOC_ONE(synblock_T);
Yegappan Lakshmanan960dcbd2023-03-07 17:45:11 +00006288 CLEAR_POINTER(curwin->w_s);
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006289 hash_init(&curwin->w_s->b_keywtab);
6290 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006291#ifdef FEAT_SPELL
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006292 // TODO: keep the spell checking as it was.
6293 curwin->w_p_spell = FALSE; // No spell checking
Bram Moolenaard1f76af2020-09-13 22:37:34 +02006294 // make sure option values are "empty_option" instead of NULL
Bram Moolenaar860cae12010-06-05 23:22:07 +02006295 clear_string_option(&curwin->w_s->b_p_spc);
6296 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006297 clear_string_option(&curwin->w_s->b_p_spl);
Bram Moolenaard1f76af2020-09-13 22:37:34 +02006298 clear_string_option(&curwin->w_s->b_p_spo);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006299#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006300 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006301 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006302
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006303 // save value of b:current_syntax
Bram Moolenaar1950c352010-06-06 15:21:10 +02006304 old_value = get_var_value((char_u *)"b:current_syntax");
6305 if (old_value != NULL)
6306 old_value = vim_strsave(old_value);
6307
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006308 // Apply the "syntax" autocommand event, this finds and loads the syntax
6309 // file.
Bram Moolenaar860cae12010-06-05 23:22:07 +02006310 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006311
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006312 // move value of b:current_syntax to w:current_syntax
Bram Moolenaar1950c352010-06-06 15:21:10 +02006313 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006314 if (new_value != NULL)
6315 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006316
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006317 // restore value of b:current_syntax
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006318 if (old_value == NULL)
6319 do_unlet((char_u *)"b:current_syntax", TRUE);
6320 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006321 {
6322 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6323 vim_free(old_value);
6324 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006325}
6326
6327 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006328syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006329{
6330 return (win->w_s->b_syn_patterns.ga_len != 0
6331 || win->w_s->b_syn_clusters.ga_len != 0
6332 || win->w_s->b_keywtab.ht_used > 0
6333 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006334}
6335
Bram Moolenaar071d4272004-06-13 20:20:40 +00006336
6337static enum
6338{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006339 EXP_SUBCMD, // expand ":syn" sub-commands
6340 EXP_CASE, // expand ":syn case" arguments
6341 EXP_SPELL, // expand ":syn spell" arguments
bfredlaf9a6002022-08-26 21:58:31 +01006342 EXP_SYNC, // expand ":syn sync" arguments
6343 EXP_CLUSTER // expand ":syn list @cluster" arguments
Bram Moolenaar071d4272004-06-13 20:20:40 +00006344} expand_what;
6345
Bram Moolenaar4f688582007-07-24 12:34:30 +00006346/*
6347 * Reset include_link, include_default, include_none to 0.
6348 * Called when we are done expanding.
6349 */
6350 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006351reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006352{
6353 include_link = include_default = include_none = 0;
6354}
6355
6356/*
6357 * Handle command line completion for :match and :echohl command: Add "None"
6358 * as highlight group.
6359 */
6360 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006361set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006362{
6363 xp->xp_context = EXPAND_HIGHLIGHT;
6364 xp->xp_pattern = arg;
6365 include_none = 1;
6366}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006367
6368/*
6369 * Handle command line completion for :syntax command.
6370 */
6371 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006372set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006373{
6374 char_u *p;
6375
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006376 // Default: expand subcommands
Bram Moolenaar071d4272004-06-13 20:20:40 +00006377 xp->xp_context = EXPAND_SYNTAX;
6378 expand_what = EXP_SUBCMD;
6379 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006380 include_link = 0;
6381 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006382
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00006383 if (*arg == NUL)
6384 return;
6385
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006386 // (part of) subcommand already typed
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00006387 p = skiptowhite(arg);
6388 if (*p == NUL)
6389 return;
6390
6391 // past first word
6392 xp->xp_pattern = skipwhite(p);
6393 if (*skiptowhite(xp->xp_pattern) != NUL)
6394 xp->xp_context = EXPAND_NOTHING;
6395 else if (STRNICMP(arg, "case", p - arg) == 0)
6396 expand_what = EXP_CASE;
6397 else if (STRNICMP(arg, "spell", p - arg) == 0)
6398 expand_what = EXP_SPELL;
6399 else if (STRNICMP(arg, "sync", p - arg) == 0)
6400 expand_what = EXP_SYNC;
6401 else if (STRNICMP(arg, "list", p - arg) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006402 {
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00006403 p = skipwhite(p);
6404 if (*p == '@')
6405 expand_what = EXP_CLUSTER;
6406 else
6407 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006408 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00006409 else if (STRNICMP(arg, "keyword", p - arg) == 0
6410 || STRNICMP(arg, "region", p - arg) == 0
6411 || STRNICMP(arg, "match", p - arg) == 0)
6412 xp->xp_context = EXPAND_HIGHLIGHT;
6413 else
6414 xp->xp_context = EXPAND_NOTHING;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006415}
6416
Bram Moolenaar071d4272004-06-13 20:20:40 +00006417/*
6418 * Function given to ExpandGeneric() to obtain the list syntax names for
6419 * expansion.
6420 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006421 char_u *
Bram Moolenaar5ff595d2022-08-26 22:36:41 +01006422get_syntax_name(expand_T *xp, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006423{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006424 switch (expand_what)
6425 {
6426 case EXP_SUBCMD:
6427 return (char_u *)subcommands[idx].name;
6428 case EXP_CASE:
6429 {
6430 static char *case_args[] = {"match", "ignore", NULL};
6431 return (char_u *)case_args[idx];
6432 }
6433 case EXP_SPELL:
6434 {
6435 static char *spell_args[] =
6436 {"toplevel", "notoplevel", "default", NULL};
6437 return (char_u *)spell_args[idx];
6438 }
6439 case EXP_SYNC:
6440 {
6441 static char *sync_args[] =
6442 {"ccomment", "clear", "fromstart",
6443 "linebreaks=", "linecont", "lines=", "match",
6444 "maxlines=", "minlines=", "region", NULL};
6445 return (char_u *)sync_args[idx];
6446 }
bfredlaf9a6002022-08-26 21:58:31 +01006447 case EXP_CLUSTER:
6448 {
6449 if (idx < curwin->w_s->b_syn_clusters.ga_len)
6450 {
Bram Moolenaar5ff595d2022-08-26 22:36:41 +01006451 vim_snprintf((char *)xp->xp_buf, EXPAND_BUF_LEN, "@%s",
bfredlaf9a6002022-08-26 21:58:31 +01006452 SYN_CLSTR(curwin->w_s)[idx].scl_name);
Bram Moolenaar5ff595d2022-08-26 22:36:41 +01006453 return xp->xp_buf;
bfredlaf9a6002022-08-26 21:58:31 +01006454 }
6455 else
6456 return NULL;
6457 }
Bram Moolenaar2d028392017-01-08 18:28:22 +01006458 }
6459 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006460}
6461
Bram Moolenaar071d4272004-06-13 20:20:40 +00006462
Bram Moolenaar071d4272004-06-13 20:20:40 +00006463/*
6464 * Function called for expression evaluation: get syntax ID at file position.
6465 */
6466 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006467syn_get_id(
6468 win_T *wp,
6469 long lnum,
6470 colnr_T col,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006471 int trans, // remove transparency
6472 int *spellp, // return: can do spell checking
6473 int keep_state) // keep state of char at "col"
Bram Moolenaar071d4272004-06-13 20:20:40 +00006474{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006475 // When the position is not after the current position and in the same
zeertzjqca7e86c2022-04-16 16:49:24 +01006476 // line of the same window with the same buffer, need to restart parsing.
6477 if (wp != syn_win
6478 || wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006479 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006480 || col < current_col)
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006481 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006482 else if (wp->w_buffer == syn_buf
6483 && lnum == current_lnum
6484 && col > current_col)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006485 // next_match may not be correct when moving around, e.g. with the
6486 // "skip" expression in searchpair()
Bram Moolenaar6773a342016-01-19 20:52:44 +01006487 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006488
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006489 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006490
6491 return (trans ? current_trans_id : current_id);
6492}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006493
Bram Moolenaar860cae12010-06-05 23:22:07 +02006494#if defined(FEAT_CONCEAL) || defined(PROTO)
6495/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006496 * Get extra information about the syntax item. Must be called right after
6497 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006498 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006499 * Returns the current flags.
6500 */
6501 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006502get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006503{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006504 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006505 return current_flags;
6506}
6507
6508/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006509 * Return conceal substitution character
6510 */
6511 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006512syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006513{
6514 return current_sub_char;
6515}
6516#endif
6517
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006518#if defined(FEAT_EVAL) || defined(PROTO)
6519/*
6520 * Return the syntax ID at position "i" in the current stack.
6521 * The caller must have called syn_get_id() before to fill the stack.
6522 * Returns -1 when "i" is out of range.
6523 */
6524 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006525syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006526{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006527 if (i >= current_state.ga_len)
6528 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006529 // Need to invalidate the state, because we didn't properly finish it
6530 // for the last character, "keep_state" was TRUE.
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006531 invalidate_current_state();
6532 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006533 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006534 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006535 return CUR_STATE(i).si_id;
6536}
6537#endif
6538
Bram Moolenaar071d4272004-06-13 20:20:40 +00006539#if defined(FEAT_FOLDING) || defined(PROTO)
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006540 static int
6541syn_cur_foldlevel(void)
6542{
6543 int level = 0;
6544 int i;
6545
6546 for (i = 0; i < current_state.ga_len; ++i)
6547 if (CUR_STATE(i).si_flags & HL_FOLD)
6548 ++level;
6549 return level;
6550}
6551
Bram Moolenaar071d4272004-06-13 20:20:40 +00006552/*
6553 * Function called to get folding level for line "lnum" in window "wp".
6554 */
6555 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006556syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006557{
6558 int level = 0;
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006559 int low_level;
6560 int cur_level;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006561
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006562 // Return quickly when there are no fold items at all.
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006563 if (wp->w_s->b_syn_folditems != 0
6564 && !wp->w_s->b_syn_error
6565# ifdef SYN_TIME_LIMIT
6566 && !wp->w_s->b_syn_slow
6567# endif
6568 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006569 {
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006570 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006571
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006572 // Start with the fold level at the start of the line.
6573 level = syn_cur_foldlevel();
6574
6575 if (wp->w_s->b_syn_foldlevel == SYNFLD_MINIMUM)
6576 {
6577 // Find the lowest fold level that is followed by a higher one.
6578 cur_level = level;
6579 low_level = cur_level;
6580 while (!current_finished)
6581 {
6582 (void)syn_current_attr(FALSE, FALSE, NULL, FALSE);
6583 cur_level = syn_cur_foldlevel();
6584 if (cur_level < low_level)
6585 low_level = cur_level;
6586 else if (cur_level > low_level)
6587 level = low_level;
6588 ++current_col;
6589 }
6590 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006591 }
6592 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006593 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006594 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006595 if (level < 0)
6596 level = 0;
6597 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006598 return level;
6599}
6600#endif
6601
Bram Moolenaar01615492015-02-03 13:00:38 +01006602#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006603/*
6604 * ":syntime".
6605 */
6606 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006607ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006608{
6609 if (STRCMP(eap->arg, "on") == 0)
6610 syn_time_on = TRUE;
6611 else if (STRCMP(eap->arg, "off") == 0)
6612 syn_time_on = FALSE;
6613 else if (STRCMP(eap->arg, "clear") == 0)
6614 syntime_clear();
6615 else if (STRCMP(eap->arg, "report") == 0)
6616 syntime_report();
6617 else
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00006618 semsg(_(e_invalid_argument_str), eap->arg);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006619}
6620
6621 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006622syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006623{
6624 profile_zero(&st->total);
6625 profile_zero(&st->slowest);
6626 st->count = 0;
6627 st->match = 0;
6628}
6629
6630/*
6631 * Clear the syntax timing for the current buffer.
6632 */
6633 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006634syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006635{
6636 int idx;
6637 synpat_T *spp;
6638
6639 if (!syntax_present(curwin))
6640 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006641 msg(_(msg_no_items));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006642 return;
6643 }
6644 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6645 {
6646 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6647 syn_clear_time(&spp->sp_time);
6648 }
6649}
6650
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006651/*
6652 * Function given to ExpandGeneric() to obtain the possible arguments of the
6653 * ":syntime {on,off,clear,report}" command.
6654 */
6655 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006656get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006657{
6658 switch (idx)
6659 {
6660 case 0: return (char_u *)"on";
6661 case 1: return (char_u *)"off";
6662 case 2: return (char_u *)"clear";
6663 case 3: return (char_u *)"report";
6664 }
6665 return NULL;
6666}
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006667
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006668typedef struct
6669{
6670 proftime_T total;
6671 int count;
6672 int match;
6673 proftime_T slowest;
6674 proftime_T average;
6675 int id;
6676 char_u *pattern;
6677} time_entry_T;
6678
6679 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006680syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006681{
6682 const time_entry_T *s1 = v1;
6683 const time_entry_T *s2 = v2;
6684
6685 return profile_cmp(&s1->total, &s2->total);
6686}
6687
6688/*
6689 * Clear the syntax timing for the current buffer.
6690 */
6691 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006692syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006693{
6694 int idx;
6695 synpat_T *spp;
Bram Moolenaar73e28dc2022-09-17 21:08:33 +01006696# if defined(FEAT_RELTIME)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006697 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006698# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006699 int len;
6700 proftime_T total_total;
6701 int total_count = 0;
6702 garray_T ga;
6703 time_entry_T *p;
6704
6705 if (!syntax_present(curwin))
6706 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006707 msg(_(msg_no_items));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006708 return;
6709 }
6710
6711 ga_init2(&ga, sizeof(time_entry_T), 50);
6712 profile_zero(&total_total);
6713 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6714 {
6715 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6716 if (spp->sp_time.count > 0)
6717 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006718 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006719 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6720 p->total = spp->sp_time.total;
6721 profile_add(&total_total, &spp->sp_time.total);
6722 p->count = spp->sp_time.count;
6723 p->match = spp->sp_time.match;
6724 total_count += spp->sp_time.count;
6725 p->slowest = spp->sp_time.slowest;
Bram Moolenaar73e28dc2022-09-17 21:08:33 +01006726# if defined(FEAT_RELTIME)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006727 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6728 p->average = tm;
6729# endif
6730 p->id = spp->sp_syn.id;
6731 p->pattern = spp->sp_pattern;
6732 ++ga.ga_len;
6733 }
6734 }
6735
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006736 // Sort on total time. Skip if there are no items to avoid passing NULL
6737 // pointer to qsort().
Bram Moolenaara2162552017-01-08 17:46:20 +01006738 if (ga.ga_len > 1)
6739 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006740 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006741
Bram Moolenaar32526b32019-01-19 17:43:09 +01006742 msg_puts_title(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6743 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006744 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6745 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006746 p = ((time_entry_T *)ga.ga_data) + idx;
6747
Bram Moolenaar32526b32019-01-19 17:43:09 +01006748 msg_puts(profile_msg(&p->total));
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006749 msg_puts(" "); // make sure there is always a separating space
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006750 msg_advance(13);
6751 msg_outnum(p->count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006752 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006753 msg_advance(20);
6754 msg_outnum(p->match);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006755 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006756 msg_advance(26);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006757 msg_puts(profile_msg(&p->slowest));
6758 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006759 msg_advance(38);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006760 msg_puts(profile_msg(&p->average));
6761 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006762 msg_advance(50);
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02006763 msg_outtrans(highlight_group_name(p->id - 1));
Bram Moolenaar32526b32019-01-19 17:43:09 +01006764 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006765
6766 msg_advance(69);
6767 if (Columns < 80)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006768 len = 20; // will wrap anyway
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006769 else
6770 len = Columns - 70;
6771 if (len > (int)STRLEN(p->pattern))
6772 len = (int)STRLEN(p->pattern);
6773 msg_outtrans_len(p->pattern, len);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006774 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006775 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006776 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006777 if (!got_int)
6778 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006779 msg_puts("\n");
6780 msg_puts(profile_msg(&total_total));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006781 msg_advance(13);
6782 msg_outnum(total_count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006783 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006784 }
6785}
6786#endif
6787
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006788#endif // FEAT_SYN_HL