blob: 7211da392c32f971ec8c21d4cda82c31339f9014 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * syntax.c: code for syntax highlighting
12 */
13
14#include "vim.h"
15
Bram Moolenaar071d4272004-06-13 20:20:40 +000016#if defined(FEAT_SYN_HL) || defined(PROTO)
17
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010018#define SYN_NAMELEN 50 // maximum length of a syntax name
Bram Moolenaar071d4272004-06-13 20:20:40 +000019
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010020// different types of offsets that are possible
21#define SPO_MS_OFF 0 // match start offset
22#define SPO_ME_OFF 1 // match end offset
23#define SPO_HS_OFF 2 // highl. start offset
24#define SPO_HE_OFF 3 // highl. end offset
25#define SPO_RS_OFF 4 // region start offset
26#define SPO_RE_OFF 5 // region end offset
27#define SPO_LC_OFF 6 // leading context offset
Bram Moolenaar071d4272004-06-13 20:20:40 +000028#define SPO_COUNT 7
29
30static char *(spo_name_tab[SPO_COUNT]) =
31 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
32
33/*
34 * The patterns that are being searched for are stored in a syn_pattern.
35 * A match item consists of one pattern.
36 * A start/end item consists of n start patterns and m end patterns.
37 * A start/skip/end item consists of n start patterns, one skip pattern and m
38 * end patterns.
39 * For the latter two, the patterns are always consecutive: start-skip-end.
40 *
41 * A character offset can be given for the matched text (_m_start and _m_end)
42 * and for the actually highlighted text (_h_start and _h_end).
Bram Moolenaar36f92302018-02-24 21:36:34 +010043 *
44 * Note that ordering of members is optimized to reduce padding.
Bram Moolenaar071d4272004-06-13 20:20:40 +000045 */
46typedef struct syn_pattern
47{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010048 char sp_type; // see SPTYPE_ defines below
49 char sp_syncing; // this item used for syncing
50 short sp_syn_match_id; // highlight group ID of pattern
51 short sp_off_flags; // see below
52 int sp_offsets[SPO_COUNT]; // offsets
53 int sp_flags; // see HL_ defines below
Bram Moolenaar860cae12010-06-05 23:22:07 +020054#ifdef FEAT_CONCEAL
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010055 int sp_cchar; // conceal substitute character
Bram Moolenaar860cae12010-06-05 23:22:07 +020056#endif
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010057 int sp_ic; // ignore-case flag for sp_prog
58 int sp_sync_idx; // sync item index (syncing only)
59 int sp_line_id; // ID of last line where tried
60 int sp_startcol; // next match in sp_line_id line
61 short *sp_cont_list; // cont. group IDs, if non-zero
62 short *sp_next_list; // next group IDs, if non-zero
63 struct sp_syn sp_syn; // struct passed to in_id_list()
64 char_u *sp_pattern; // regexp to match, pattern
65 regprog_T *sp_prog; // regexp to match, program
Bram Moolenaarf7512552013-06-06 14:55:19 +020066#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +020067 syn_time_T sp_time;
68#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000069} synpat_T;
70
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010071// The sp_off_flags are computed like this:
72// offset from the start of the matched text: (1 << SPO_XX_OFF)
73// offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
74// When both are present, only one is used.
Bram Moolenaar071d4272004-06-13 20:20:40 +000075
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010076#define SPTYPE_MATCH 1 // match keyword with this group ID
77#define SPTYPE_START 2 // match a regexp, start of item
78#define SPTYPE_END 3 // match a regexp, end of item
79#define SPTYPE_SKIP 4 // match a regexp, skip within item
Bram Moolenaar071d4272004-06-13 20:20:40 +000080
Bram Moolenaar071d4272004-06-13 20:20:40 +000081
82#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
83
kylo252ae6f1d82022-02-16 19:24:07 +000084#define NONE_IDX (-2) // value of sp_sync_idx for "NONE"
Bram Moolenaar071d4272004-06-13 20:20:40 +000085
86/*
87 * Flags for b_syn_sync_flags:
88 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010089#define SF_CCOMMENT 0x01 // sync on a C-style comment
90#define SF_MATCH 0x02 // sync by matching a pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +000091
92#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
93
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010094#define MAXKEYWLEN 80 // maximum length of a keyword
Bram Moolenaar071d4272004-06-13 20:20:40 +000095
96/*
97 * The attributes of the syntax item that has been recognized.
98 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010099static int current_attr = 0; // attr of current syntax word
Bram Moolenaar071d4272004-06-13 20:20:40 +0000100#ifdef FEAT_EVAL
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100101static int current_id = 0; // ID of current char for syn_get_id()
102static int current_trans_id = 0; // idem, transparency removed
Bram Moolenaar071d4272004-06-13 20:20:40 +0000103#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200104#ifdef FEAT_CONCEAL
105static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200106static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200107static int current_sub_char = 0;
108#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000109
Bram Moolenaar217ad922005-03-20 22:37:15 +0000110typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000111{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100112 char_u *scl_name; // syntax cluster name
113 char_u *scl_name_u; // uppercase of scl_name
114 short *scl_list; // IDs in this syntax cluster
Bram Moolenaar217ad922005-03-20 22:37:15 +0000115} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000116
117/*
118 * Methods of combining two clusters
119 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100120#define CLUSTER_REPLACE 1 // replace first list with second
121#define CLUSTER_ADD 2 // add second list to first
122#define CLUSTER_SUBTRACT 3 // subtract second list from first
Bram Moolenaar071d4272004-06-13 20:20:40 +0000123
Bram Moolenaar217ad922005-03-20 22:37:15 +0000124#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000125
126/*
127 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200128 * 0 - 19999 normal syntax groups
129 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
130 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
131 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
132 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000133 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100134#define SYNID_ALLBUT MAX_HL_ID // syntax group ID for contains=ALLBUT
135#define SYNID_TOP 21000 // syntax group ID for contains=TOP
136#define SYNID_CONTAINED 22000 // syntax group ID for contains=CONTAINED
137#define SYNID_CLUSTER 23000 // first syntax group ID for clusters
Bram Moolenaar42431a72011-04-01 14:44:59 +0200138
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100139#define MAX_SYN_INC_TAG 999 // maximum before the above overflow
Bram Moolenaar42431a72011-04-01 14:44:59 +0200140#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000141
142/*
143 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
144 * expand_filename(). Most of the other syntax commands don't need it, so
145 * instead of passing it to them, we stow it here.
146 */
147static char_u **syn_cmdlinep;
148
149/*
150 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200151 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000152 * rules in each ":syn include"'d file.
153 */
154static int current_syn_inc_tag = 0;
155static int running_syn_inc_tag = 0;
156
157/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000158 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
159 * This avoids adding a pointer to the hashtable item.
160 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
161 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
162 * HI2KE() converts a hashitem pointer to a var pointer.
163 */
164static keyentry_T dumkey;
165#define KE2HIKEY(kp) ((kp)->keyword)
166#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
167#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
168
169/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000170 * To reduce the time spent in keepend(), remember at which level in the state
171 * stack the first item with "keepend" is present. When "-1", there is no
172 * "keepend" on the stack.
173 */
174static int keepend_level = -1;
175
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200176static char msg_no_items[] = N_("No Syntax items defined for this buffer");
177
Bram Moolenaar071d4272004-06-13 20:20:40 +0000178/*
179 * For the current state we need to remember more than just the idx.
180 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
181 * (The end positions have the column number of the next char)
182 */
183typedef struct state_item
184{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100185 int si_idx; // index of syntax pattern or
186 // KEYWORD_IDX
187 int si_id; // highlight group ID for keywords
188 int si_trans_id; // idem, transparency removed
189 int si_m_lnum; // lnum of the match
190 int si_m_startcol; // starting column of the match
191 lpos_T si_m_endpos; // just after end posn of the match
192 lpos_T si_h_startpos; // start position of the highlighting
193 lpos_T si_h_endpos; // end position of the highlighting
194 lpos_T si_eoe_pos; // end position of end pattern
195 int si_end_idx; // group ID for end pattern or zero
196 int si_ends; // if match ends before si_m_endpos
197 int si_attr; // attributes in this state
198 long si_flags; // HL_HAS_EOL flag in this state, and
199 // HL_SKIP* for si_next_list
Bram Moolenaar860cae12010-06-05 23:22:07 +0200200#ifdef FEAT_CONCEAL
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100201 int si_seqnr; // sequence number
202 int si_cchar; // substitution character for conceal
Bram Moolenaar860cae12010-06-05 23:22:07 +0200203#endif
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100204 short *si_cont_list; // list of contained groups
205 short *si_next_list; // nextgroup IDs after this item ends
206 reg_extmatch_T *si_extmatch; // \z(...\) matches from start
207 // pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +0000208} stateitem_T;
209
kylo252ae6f1d82022-02-16 19:24:07 +0000210#define KEYWORD_IDX (-1) // value of si_idx for keywords
211#define ID_LIST_ALL ((short *)-1) // valid of si_cont_list for containing all
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100212 // but contained groups
Bram Moolenaar071d4272004-06-13 20:20:40 +0000213
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200214#ifdef FEAT_CONCEAL
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100215static int next_seqnr = 1; // value to use for si_seqnr
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200216#endif
217
Bram Moolenaar071d4272004-06-13 20:20:40 +0000218/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000219 * Struct to reduce the number of arguments to get_syn_options(), it's used
220 * very often.
221 */
222typedef struct
223{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100224 int flags; // flags for contained and transparent
225 int keyword; // TRUE for ":syn keyword"
226 int *sync_idx; // syntax item for "grouphere" argument, NULL
227 // if not allowed
228 char has_cont_list; // TRUE if "cont_list" can be used
229 short *cont_list; // group IDs for "contains" argument
230 short *cont_in_list; // group IDs for "containedin" argument
231 short *next_list; // group IDs for "nextgroup" argument
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000232} syn_opt_arg_T;
233
234/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000235 * The next possible match in the current line for any pattern is remembered,
236 * to avoid having to try for a match in each column.
237 * If next_match_idx == -1, not tried (in this line) yet.
238 * If next_match_col == MAXCOL, no match found in this line.
239 * (All end positions have the column of the char after the end)
240 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100241static int next_match_col; // column for start of next match
242static lpos_T next_match_m_endpos; // position for end of next match
243static lpos_T next_match_h_startpos; // pos. for highl. start of next match
244static lpos_T next_match_h_endpos; // pos. for highl. end of next match
245static int next_match_idx; // index of matched item
246static long next_match_flags; // flags for next match
247static lpos_T next_match_eos_pos; // end of start pattn (start region)
248static lpos_T next_match_eoe_pos; // pos. for end of end pattern
249static int next_match_end_idx; // ID of group for end pattn or zero
Bram Moolenaar071d4272004-06-13 20:20:40 +0000250static reg_extmatch_T *next_match_extmatch = NULL;
251
252/*
253 * A state stack is an array of integers or stateitem_T, stored in a
Bram Moolenaarf86db782018-10-25 13:31:37 +0200254 * garray_T. A state stack is invalid if its itemsize entry is zero.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000255 */
256#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
257#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
258
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200259#define FOR_ALL_SYNSTATES(sb, sst) \
260 for ((sst) = (sb)->b_sst_first; (sst) != NULL; (sst) = (sst)->sst_next)
261
Bram Moolenaar071d4272004-06-13 20:20:40 +0000262/*
263 * The current state (within the line) of the recognition engine.
264 * When current_state.ga_itemsize is 0 the current state is invalid.
265 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100266static win_T *syn_win; // current window for highlighting
267static buf_T *syn_buf; // current buffer for highlighting
268static synblock_T *syn_block; // current buffer for highlighting
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100269static linenr_T current_lnum = 0; // lnum of current state
270static colnr_T current_col = 0; // column of current state
271static int current_state_stored = 0; // TRUE if stored current state
272 // after setting current_finished
273static int current_finished = 0; // current line has been finished
274static garray_T current_state // current stack of state_items
Bram Moolenaar071d4272004-06-13 20:20:40 +0000275 = {0, 0, 0, 0, NULL};
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100276static short *current_next_list = NULL; // when non-zero, nextgroup list
277static int current_next_flags = 0; // flags for current_next_list
278static int current_line_id = 0; // unique number for current line
Bram Moolenaar071d4272004-06-13 20:20:40 +0000279
280#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
281
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100282static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100283static int syn_match_linecont(linenr_T lnum);
284static void syn_start_line(void);
285static void syn_update_ends(int startofline);
286static void syn_stack_alloc(void);
287static int syn_stack_cleanup(void);
288static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
289static synstate_T *syn_stack_find_entry(linenr_T lnum);
290static synstate_T *store_current_state(void);
291static void load_current_state(synstate_T *from);
292static void invalidate_current_state(void);
293static int syn_stack_equal(synstate_T *sp);
294static void validate_current_state(void);
295static int syn_finish_line(int syncing);
296static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
297static int did_match_already(int idx, garray_T *gap);
298static stateitem_T *push_next_match(stateitem_T *cur_si);
299static void check_state_ends(void);
300static void update_si_attr(int idx);
301static void check_keepend(void);
302static void update_si_end(stateitem_T *sip, int startcol, int force);
303static short *copy_id_list(short *list);
304static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
305static int push_current_state(int idx);
306static void pop_current_state(void);
Bram Moolenaarf7512552013-06-06 14:55:19 +0200307#ifdef FEAT_PROFILE
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100308static void syn_clear_time(syn_time_T *tt);
309static void syntime_clear(void);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100310static void syntime_report(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200311static int syn_time_on = FALSE;
312# define IF_SYN_TIME(p) (p)
313#else
314# define IF_SYN_TIME(p) NULL
315typedef int syn_time_T;
316#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000317
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100318static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
319static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_endpos, long *flagsp, lpos_T *end_endpos, int *end_idx, reg_extmatch_T *start_ext);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000320
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100321static void limit_pos(lpos_T *pos, lpos_T *limit);
322static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
323static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
324static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
325static char_u *syn_getcurline(void);
326static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
327static int check_keyword_id(char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100328static void syn_remove_pattern(synblock_T *block, int idx);
329static void syn_clear_pattern(synblock_T *block, int i);
330static void syn_clear_cluster(synblock_T *block, int i);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100331static void syn_clear_one(int id, int syncing);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100332static void syn_cmd_onoff(exarg_T *eap, char *name);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100333static void syn_lines_msg(void);
334static void syn_match_msg(void);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100335static void syn_list_one(int id, int syncing, int link_only);
336static void syn_list_cluster(int id);
337static void put_id_list(char_u *name, short *list, int attr);
338static void put_pattern(char *s, int c, synpat_T *spp, int attr);
339static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
340static void syn_clear_keyword(int id, hashtab_T *ht);
341static void clear_keywtab(hashtab_T *ht);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100342static int syn_scl_namen2id(char_u *linep, int len);
343static int syn_check_cluster(char_u *pp, int len);
344static int syn_add_cluster(char_u *name);
345static void init_syn_patterns(void);
346static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100347static int get_id_list(char_u **arg, int keylen, short **list, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100348static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000349
350/*
351 * Start the syntax recognition for a line. This function is normally called
352 * from the screen updating, once for each displayed line.
353 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
354 * it. Careful: curbuf and curwin are likely to point to another buffer and
355 * window.
356 */
357 void
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200358syntax_start(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000359{
360 synstate_T *p;
361 synstate_T *last_valid = NULL;
362 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000363 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000364 linenr_T parsed_lnum;
365 linenr_T first_stored;
366 int dist;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100367 static varnumber_T changedtick = 0; // remember the last change ID
Bram Moolenaar071d4272004-06-13 20:20:40 +0000368
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200369#ifdef FEAT_CONCEAL
370 current_sub_char = NUL;
371#endif
372
Bram Moolenaar071d4272004-06-13 20:20:40 +0000373 /*
374 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000375 * Also do this when a change was made, the current state may be invalid
376 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000377 */
Bram Moolenaarb681be12016-03-31 23:02:16 +0200378 if (syn_block != wp->w_s
379 || syn_buf != wp->w_buffer
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100380 || changedtick != CHANGEDTICK(syn_buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000381 {
382 invalidate_current_state();
383 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200384 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000385 }
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100386 changedtick = CHANGEDTICK(syn_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000387 syn_win = wp;
388
389 /*
390 * Allocate syntax stack when needed.
391 */
392 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200393 if (syn_block->b_sst_array == NULL)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100394 return; // out of memory
Bram Moolenaar860cae12010-06-05 23:22:07 +0200395 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000396
397 /*
398 * If the state of the end of the previous line is useful, store it.
399 */
400 if (VALID_STATE(&current_state)
401 && current_lnum < lnum
402 && current_lnum < syn_buf->b_ml.ml_line_count)
403 {
404 (void)syn_finish_line(FALSE);
405 if (!current_state_stored)
406 {
407 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000408 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000409 }
410
411 /*
412 * If the current_lnum is now the same as "lnum", keep the current
413 * state (this happens very often!). Otherwise invalidate
414 * current_state and figure it out below.
415 */
416 if (current_lnum != lnum)
417 invalidate_current_state();
418 }
419 else
420 invalidate_current_state();
421
422 /*
423 * Try to synchronize from a saved state in b_sst_array[].
424 * Only do this if lnum is not before and not to far beyond a saved state.
425 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200426 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000427 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100428 // Find last valid saved state before start_lnum.
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200429 FOR_ALL_SYNSTATES(syn_block, p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000430 {
431 if (p->sst_lnum > lnum)
432 break;
433 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
434 {
435 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200436 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000437 last_min_valid = p;
438 }
439 }
440 if (last_min_valid != NULL)
441 load_current_state(last_min_valid);
442 }
443
444 /*
445 * If "lnum" is before or far beyond a line with a saved state, need to
446 * re-synchronize.
447 */
448 if (INVALID_STATE(&current_state))
449 {
450 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200451 if (current_lnum == 1)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100452 // First line is always valid, no matter "minlines".
Bram Moolenaard6761c32011-06-19 04:54:21 +0200453 first_stored = 1;
454 else
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100455 // Need to parse "minlines" lines before state can be considered
456 // valid to store.
Bram Moolenaard6761c32011-06-19 04:54:21 +0200457 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000458 }
459 else
460 first_stored = current_lnum;
461
462 /*
463 * Advance from the sync point or saved state until the current line.
464 * Save some entries for syncing with later on.
465 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200466 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000467 dist = 999999;
468 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200469 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000470 while (current_lnum < lnum)
471 {
472 syn_start_line();
473 (void)syn_finish_line(FALSE);
474 ++current_lnum;
475
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100476 // If we parsed at least "minlines" lines or started at a valid
477 // state, the current state is considered valid.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000478 if (current_lnum >= first_stored)
479 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100480 // Check if the saved state entry is for the current line and is
481 // equal to the current state. If so, then validate all saved
482 // states that depended on a change before the parsed line.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000483 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000484 prev = syn_stack_find_entry(current_lnum - 1);
485 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200486 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000487 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000488 sp = prev;
489 while (sp != NULL && sp->sst_lnum < current_lnum)
490 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000491 if (sp != NULL
492 && sp->sst_lnum == current_lnum
493 && syn_stack_equal(sp))
494 {
495 parsed_lnum = current_lnum;
496 prev = sp;
497 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
498 {
499 if (sp->sst_lnum <= lnum)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100500 // valid state before desired line, use this one
Bram Moolenaar071d4272004-06-13 20:20:40 +0000501 prev = sp;
502 else if (sp->sst_change_lnum == 0)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100503 // past saved states depending on change, break here.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000504 break;
505 sp->sst_change_lnum = 0;
506 sp = sp->sst_next;
507 }
508 load_current_state(prev);
509 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100510 // Store the state at this line when it's the first one, the line
511 // where we start parsing, or some distance from the previously
512 // saved state. But only when parsed at least 'minlines'.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000513 else if (prev == NULL
514 || current_lnum == lnum
515 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000516 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000517 }
518
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100519 // This can take a long time: break when CTRL-C pressed. The current
520 // state will be wrong then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000521 line_breakcheck();
522 if (got_int)
523 {
524 current_lnum = lnum;
525 break;
526 }
527 }
528
529 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000530}
531
532/*
533 * We cannot simply discard growarrays full of state_items or buf_states; we
534 * have to manually release their extmatch pointers first.
535 */
536 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100537clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000538{
539 int i;
540 garray_T *gap;
541
542 if (p->sst_stacksize > SST_FIX_STATES)
543 {
544 gap = &(p->sst_union.sst_ga);
545 for (i = 0; i < gap->ga_len; i++)
546 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
547 ga_clear(gap);
548 }
549 else
550 {
551 for (i = 0; i < p->sst_stacksize; i++)
552 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
553 }
554}
555
556/*
557 * Cleanup the current_state stack.
558 */
559 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100560clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000561{
562 int i;
563 stateitem_T *sip;
564
565 sip = (stateitem_T *)(current_state.ga_data);
566 for (i = 0; i < current_state.ga_len; i++)
567 unref_extmatch(sip[i].si_extmatch);
568 ga_clear(&current_state);
569}
570
571/*
572 * Try to find a synchronisation point for line "lnum".
573 *
574 * This sets current_lnum and the current state. One of three methods is
575 * used:
576 * 1. Search backwards for the end of a C-comment.
577 * 2. Search backwards for given sync patterns.
578 * 3. Simply start on a given number of lines above "lnum".
579 */
580 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100581syn_sync(
582 win_T *wp,
583 linenr_T start_lnum,
584 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000585{
586 buf_T *curbuf_save;
587 win_T *curwin_save;
588 pos_T cursor_save;
589 int idx;
590 linenr_T lnum;
591 linenr_T end_lnum;
592 linenr_T break_lnum;
593 int had_sync_point;
594 stateitem_T *cur_si;
595 synpat_T *spp;
596 char_u *line;
597 int found_flags = 0;
598 int found_match_idx = 0;
599 linenr_T found_current_lnum = 0;
600 int found_current_col= 0;
601 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000602 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000603
604 /*
605 * Clear any current state that might be hanging around.
606 */
607 invalidate_current_state();
608
609 /*
610 * Start at least "minlines" back. Default starting point for parsing is
611 * there.
612 * Start further back, to avoid that scrolling backwards will result in
613 * resyncing for every line. Now it resyncs only one out of N lines,
614 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
615 * Watch out for overflow when minlines is MAXLNUM.
616 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200617 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000618 start_lnum = 1;
619 else
620 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200621 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000622 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200623 else if (syn_block->b_syn_sync_minlines < 10)
624 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000625 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200626 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
627 if (syn_block->b_syn_sync_maxlines != 0
628 && lnum > syn_block->b_syn_sync_maxlines)
629 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000630 if (lnum >= start_lnum)
631 start_lnum = 1;
632 else
633 start_lnum -= lnum;
634 }
635 current_lnum = start_lnum;
636
637 /*
638 * 1. Search backwards for the end of a C-style comment.
639 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200640 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000641 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100642 // Need to make syn_buf the current buffer for a moment, to be able to
643 // use find_start_comment().
Bram Moolenaar071d4272004-06-13 20:20:40 +0000644 curwin_save = curwin;
645 curwin = wp;
646 curbuf_save = curbuf;
647 curbuf = syn_buf;
648
649 /*
650 * Skip lines that end in a backslash.
651 */
652 for ( ; start_lnum > 1; --start_lnum)
653 {
654 line = ml_get(start_lnum - 1);
655 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
656 break;
657 }
658 current_lnum = start_lnum;
659
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100660 // set cursor to start of search
Bram Moolenaar071d4272004-06-13 20:20:40 +0000661 cursor_save = wp->w_cursor;
662 wp->w_cursor.lnum = start_lnum;
663 wp->w_cursor.col = 0;
664
665 /*
666 * If the line is inside a comment, need to find the syntax item that
667 * defines the comment.
668 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
669 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200670 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000671 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200672 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
673 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
674 == syn_block->b_syn_sync_id
675 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000676 {
677 validate_current_state();
678 if (push_current_state(idx) == OK)
679 update_si_attr(current_state.ga_len - 1);
680 break;
681 }
682 }
683
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100684 // restore cursor and buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +0000685 wp->w_cursor = cursor_save;
686 curwin = curwin_save;
687 curbuf = curbuf_save;
688 }
689
690 /*
691 * 2. Search backwards for given sync patterns.
692 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200693 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000694 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200695 if (syn_block->b_syn_sync_maxlines != 0
696 && start_lnum > syn_block->b_syn_sync_maxlines)
697 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000698 else
699 break_lnum = 0;
700
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000701 found_m_endpos.lnum = 0;
702 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000703 end_lnum = start_lnum;
704 lnum = start_lnum;
705 while (--lnum > break_lnum)
706 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100707 // This can take a long time: break when CTRL-C pressed.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000708 line_breakcheck();
709 if (got_int)
710 {
711 invalidate_current_state();
712 current_lnum = start_lnum;
713 break;
714 }
715
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100716 // Check if we have run into a valid saved state stack now.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000717 if (last_valid != NULL && lnum == last_valid->sst_lnum)
718 {
719 load_current_state(last_valid);
720 break;
721 }
722
723 /*
724 * Check if the previous line has the line-continuation pattern.
725 */
726 if (lnum > 1 && syn_match_linecont(lnum - 1))
727 continue;
728
729 /*
730 * Start with nothing on the state stack
731 */
732 validate_current_state();
733
734 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
735 {
736 syn_start_line();
737 for (;;)
738 {
739 had_sync_point = syn_finish_line(TRUE);
740 /*
741 * When a sync point has been found, remember where, and
742 * continue to look for another one, further on in the line.
743 */
744 if (had_sync_point && current_state.ga_len)
745 {
746 cur_si = &CUR_STATE(current_state.ga_len - 1);
747 if (cur_si->si_m_endpos.lnum > start_lnum)
748 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100749 // ignore match that goes to after where started
Bram Moolenaar071d4272004-06-13 20:20:40 +0000750 current_lnum = end_lnum;
751 break;
752 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000753 if (cur_si->si_idx < 0)
754 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100755 // Cannot happen?
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000756 found_flags = 0;
757 found_match_idx = KEYWORD_IDX;
758 }
759 else
760 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200761 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000762 found_flags = spp->sp_flags;
763 found_match_idx = spp->sp_sync_idx;
764 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000765 found_current_lnum = current_lnum;
766 found_current_col = current_col;
767 found_m_endpos = cur_si->si_m_endpos;
768 /*
769 * Continue after the match (be aware of a zero-length
770 * match).
771 */
772 if (found_m_endpos.lnum > current_lnum)
773 {
774 current_lnum = found_m_endpos.lnum;
775 current_col = found_m_endpos.col;
776 if (current_lnum >= end_lnum)
777 break;
778 }
779 else if (found_m_endpos.col > current_col)
780 current_col = found_m_endpos.col;
781 else
782 ++current_col;
783
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100784 // syn_current_attr() will have skipped the check for
785 // an item that ends here, need to do that now. Be
786 // careful not to go past the NUL.
Bram Moolenaar81366db2005-07-24 21:16:51 +0000787 prev_current_col = current_col;
788 if (syn_getcurline()[current_col] != NUL)
789 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000790 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000791 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000792 }
793 else
794 break;
795 }
796 }
797
798 /*
799 * If a sync point was encountered, break here.
800 */
801 if (found_flags)
802 {
803 /*
804 * Put the item that was specified by the sync point on the
805 * state stack. If there was no item specified, make the
806 * state stack empty.
807 */
808 clear_current_state();
809 if (found_match_idx >= 0
810 && push_current_state(found_match_idx) == OK)
811 update_si_attr(current_state.ga_len - 1);
812
813 /*
814 * When using "grouphere", continue from the sync point
815 * match, until the end of the line. Parsing starts at
816 * the next line.
817 * For "groupthere" the parsing starts at start_lnum.
818 */
819 if (found_flags & HL_SYNC_HERE)
820 {
821 if (current_state.ga_len)
822 {
823 cur_si = &CUR_STATE(current_state.ga_len - 1);
824 cur_si->si_h_startpos.lnum = found_current_lnum;
825 cur_si->si_h_startpos.col = found_current_col;
826 update_si_end(cur_si, (int)current_col, TRUE);
827 check_keepend();
828 }
829 current_col = found_m_endpos.col;
830 current_lnum = found_m_endpos.lnum;
831 (void)syn_finish_line(FALSE);
832 ++current_lnum;
833 }
834 else
835 current_lnum = start_lnum;
836
837 break;
838 }
839
840 end_lnum = lnum;
841 invalidate_current_state();
842 }
843
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100844 // Ran into start of the file or exceeded maximum number of lines
Bram Moolenaar071d4272004-06-13 20:20:40 +0000845 if (lnum <= break_lnum)
846 {
847 invalidate_current_state();
848 current_lnum = break_lnum + 1;
849 }
850 }
851
852 validate_current_state();
853}
854
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100855 static void
856save_chartab(char_u *chartab)
857{
858 if (syn_block->b_syn_isk != empty_option)
859 {
860 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
861 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
862 (size_t)32);
863 }
864}
865
866 static void
867restore_chartab(char_u *chartab)
868{
869 if (syn_win->w_s->b_syn_isk != empty_option)
870 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
871}
872
Bram Moolenaar071d4272004-06-13 20:20:40 +0000873/*
874 * Return TRUE if the line-continuation pattern matches in line "lnum".
875 */
876 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100877syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000878{
879 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100880 int r;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100881 char_u buf_chartab[32]; // chartab array for syn iskyeyword
Bram Moolenaar071d4272004-06-13 20:20:40 +0000882
Bram Moolenaar860cae12010-06-05 23:22:07 +0200883 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000884 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100885 // use syntax iskeyword option
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100886 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200887 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
888 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100889 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200890 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100891 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100892 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100893 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000894 }
895 return FALSE;
896}
897
898/*
899 * Prepare the current state for the start of a line.
900 */
901 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100902syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000903{
904 current_finished = FALSE;
905 current_col = 0;
906
907 /*
908 * Need to update the end of a start/skip/end that continues from the
909 * previous line and regions that have "keepend".
910 */
911 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +0200912 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000913 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +0200914 check_state_ends();
915 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000916
917 next_match_idx = -1;
918 ++current_line_id;
Bram Moolenaarea20de82017-06-24 22:52:24 +0200919#ifdef FEAT_CONCEAL
Bram Moolenaarcc0750d2017-06-24 22:29:24 +0200920 next_seqnr = 1;
Bram Moolenaarea20de82017-06-24 22:52:24 +0200921#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000922}
923
924/*
925 * Check for items in the stack that need their end updated.
926 * When "startofline" is TRUE the last item is always updated.
927 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
928 */
929 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100930syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000931{
932 stateitem_T *cur_si;
933 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000934 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000935
936 if (startofline)
937 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100938 // Check for a match carried over from a previous line with a
939 // contained region. The match ends as soon as the region ends.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000940 for (i = 0; i < current_state.ga_len; ++i)
941 {
942 cur_si = &CUR_STATE(i);
943 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +0200944 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +0000945 == SPTYPE_MATCH
946 && cur_si->si_m_endpos.lnum < current_lnum)
947 {
948 cur_si->si_flags |= HL_MATCHCONT;
949 cur_si->si_m_endpos.lnum = 0;
950 cur_si->si_m_endpos.col = 0;
951 cur_si->si_h_endpos = cur_si->si_m_endpos;
952 cur_si->si_ends = TRUE;
953 }
954 }
955 }
956
957 /*
958 * Need to update the end of a start/skip/end that continues from the
959 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000960 * influence contained items. If we've just removed "extend"
961 * (startofline == 0) then we should update ends of normal regions
962 * contained inside "keepend" because "extend" could have extended
963 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000964 * Then check for items ending in column 0.
965 */
966 i = current_state.ga_len - 1;
967 if (keepend_level >= 0)
968 for ( ; i > keepend_level; --i)
969 if (CUR_STATE(i).si_flags & HL_EXTEND)
970 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000971
972 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000973 for ( ; i < current_state.ga_len; ++i)
974 {
975 cur_si = &CUR_STATE(i);
976 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000977 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000978 || (i == current_state.ga_len - 1 && startofline))
979 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100980 cur_si->si_h_startpos.col = 0; // start highl. in col 0
Bram Moolenaar071d4272004-06-13 20:20:40 +0000981 cur_si->si_h_startpos.lnum = current_lnum;
982
983 if (!(cur_si->si_flags & HL_MATCHCONT))
984 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000985
986 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
987 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000988 }
989 }
990 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000991}
992
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100993/////////////////////////////////////////
994// Handling of the state stack cache.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000995
996/*
997 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
998 *
999 * To speed up syntax highlighting, the state stack for the start of some
1000 * lines is cached. These entries can be used to start parsing at that point.
1001 *
1002 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1003 * valid entries. b_sst_first points to the first one, then follow sst_next.
1004 * The entries are sorted on line number. The first entry is often for line 2
1005 * (line 1 always starts with an empty stack).
1006 * There is also a list for free entries. This construction is used to avoid
1007 * having to allocate and free memory blocks too often.
1008 *
1009 * When making changes to the buffer, this is logged in b_mod_*. When calling
1010 * update_screen() to update the display, it will call
1011 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1012 * entries. The entries which are inside the changed area are removed,
1013 * because they must be recomputed. Entries below the changed have their line
1014 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1015 * set to indicate that a check must be made if the changed lines would change
1016 * the cached entry.
1017 *
1018 * When later displaying lines, an entry is stored for each line. Displayed
1019 * lines are likely to be displayed again, in which case the state at the
1020 * start of the line is needed.
1021 * For not displayed lines, an entry is stored for every so many lines. These
1022 * entries will be used e.g., when scrolling backwards. The distance between
1023 * entries depends on the number of lines in the buffer. For small buffers
1024 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1025 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1026 */
1027
Bram Moolenaar860cae12010-06-05 23:22:07 +02001028 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001029syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001030{
1031 synstate_T *p;
1032
1033 if (block->b_sst_array != NULL)
1034 {
Bram Moolenaar00d253e2020-04-06 22:13:01 +02001035 FOR_ALL_SYNSTATES(block, p)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001036 clear_syn_state(p);
Bram Moolenaard23a8232018-02-10 18:45:26 +01001037 VIM_CLEAR(block->b_sst_array);
Bram Moolenaar95892c22018-09-28 22:26:54 +02001038 block->b_sst_first = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001039 block->b_sst_len = 0;
1040 }
1041}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001042/*
1043 * Free b_sst_array[] for buffer "buf".
1044 * Used when syntax items changed to force resyncing everywhere.
1045 */
1046 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001047syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001048{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001049#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001050 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001051#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001052
Bram Moolenaar860cae12010-06-05 23:22:07 +02001053 syn_stack_free_block(block);
1054
Bram Moolenaar071d4272004-06-13 20:20:40 +00001055#ifdef FEAT_FOLDING
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001056 // When using "syntax" fold method, must update all folds.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001057 FOR_ALL_WINDOWS(wp)
1058 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001059 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001060 foldUpdateAll(wp);
1061 }
1062#endif
1063}
1064
1065/*
1066 * Allocate the syntax state stack for syn_buf when needed.
1067 * If the number of entries in b_sst_array[] is much too big or a bit too
1068 * small, reallocate it.
1069 * Also used to allocate b_sst_array[] for the first time.
1070 */
1071 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001072syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001073{
1074 long len;
1075 synstate_T *to, *from;
1076 synstate_T *sstp;
1077
1078 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1079 if (len < SST_MIN_ENTRIES)
1080 len = SST_MIN_ENTRIES;
1081 else if (len > SST_MAX_ENTRIES)
1082 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001083 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001084 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001085 // Allocate 50% too much, to avoid reallocating too often.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001086 len = syn_buf->b_ml.ml_line_count;
1087 len = (len + len / 2) / SST_DIST + Rows * 2;
1088 if (len < SST_MIN_ENTRIES)
1089 len = SST_MIN_ENTRIES;
1090 else if (len > SST_MAX_ENTRIES)
1091 len = SST_MAX_ENTRIES;
1092
Bram Moolenaar860cae12010-06-05 23:22:07 +02001093 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001094 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001095 // When shrinking the array, cleanup the existing stack.
1096 // Make sure that all valid entries fit in the new array.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001097 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001098 && syn_stack_cleanup())
1099 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001100 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1101 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001102 }
1103
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001104 sstp = ALLOC_CLEAR_MULT(synstate_T, len);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001105 if (sstp == NULL) // out of memory!
Bram Moolenaar071d4272004-06-13 20:20:40 +00001106 return;
1107
1108 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001109 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001110 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001111 // Move the states from the old array to the new one.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001112 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001113 from = from->sst_next)
1114 {
1115 ++to;
1116 *to = *from;
1117 to->sst_next = to + 1;
1118 }
1119 }
1120 if (to != sstp - 1)
1121 {
1122 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001123 syn_block->b_sst_first = sstp;
1124 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001125 }
1126 else
1127 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001128 syn_block->b_sst_first = NULL;
1129 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001130 }
1131
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001132 // Create the list of free entries.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001133 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001134 while (++to < sstp + len)
1135 to->sst_next = to + 1;
1136 (sstp + len - 1)->sst_next = NULL;
1137
Bram Moolenaar860cae12010-06-05 23:22:07 +02001138 vim_free(syn_block->b_sst_array);
1139 syn_block->b_sst_array = sstp;
1140 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001141 }
1142}
1143
1144/*
1145 * Check for changes in a buffer to affect stored syntax states. Uses the
1146 * b_mod_* fields.
1147 * Called from update_screen(), before screen is being updated, once for each
1148 * displayed buffer.
1149 */
1150 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001151syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001152{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001153 win_T *wp;
1154
1155 syn_stack_apply_changes_block(&buf->b_s, buf);
1156
1157 FOR_ALL_WINDOWS(wp)
1158 {
1159 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1160 syn_stack_apply_changes_block(wp->w_s, buf);
1161 }
1162}
1163
1164 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001165syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001166{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001167 synstate_T *p, *prev, *np;
1168 linenr_T n;
1169
Bram Moolenaar071d4272004-06-13 20:20:40 +00001170 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001171 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001172 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001173 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001174 {
1175 n = p->sst_lnum + buf->b_mod_xlines;
1176 if (n <= buf->b_mod_bot)
1177 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001178 // this state is inside the changed area, remove it
Bram Moolenaar071d4272004-06-13 20:20:40 +00001179 np = p->sst_next;
1180 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001181 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001182 else
1183 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001184 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001185 p = np;
1186 continue;
1187 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001188 // This state is below the changed area. Remember the line
1189 // that needs to be parsed before this entry can be made valid
1190 // again.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001191 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1192 {
1193 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1194 p->sst_change_lnum += buf->b_mod_xlines;
1195 else
1196 p->sst_change_lnum = buf->b_mod_top;
1197 }
1198 if (p->sst_change_lnum == 0
1199 || p->sst_change_lnum < buf->b_mod_bot)
1200 p->sst_change_lnum = buf->b_mod_bot;
1201
1202 p->sst_lnum = n;
1203 }
1204 prev = p;
1205 p = p->sst_next;
1206 }
1207}
1208
1209/*
1210 * Reduce the number of entries in the state stack for syn_buf.
1211 * Returns TRUE if at least one entry was freed.
1212 */
1213 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001214syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001215{
1216 synstate_T *p, *prev;
1217 disptick_T tick;
1218 int above;
1219 int dist;
1220 int retval = FALSE;
1221
Bram Moolenaar95892c22018-09-28 22:26:54 +02001222 if (syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001223 return retval;
1224
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001225 // Compute normal distance between non-displayed entries.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001226 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001227 dist = 999999;
1228 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001229 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001230
1231 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001232 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001233 * be removed. Set "above" when the "tick" for the oldest entry is above
1234 * "b_sst_lasttick" (the display tick wraps around).
1235 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001236 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001237 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001238 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001239 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1240 {
1241 if (prev->sst_lnum + dist > p->sst_lnum)
1242 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001243 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244 {
1245 if (!above || p->sst_tick < tick)
1246 tick = p->sst_tick;
1247 above = TRUE;
1248 }
1249 else if (!above && p->sst_tick < tick)
1250 tick = p->sst_tick;
1251 }
1252 }
1253
1254 /*
1255 * Go through the list to make the entries for the oldest tick at an
1256 * interval of several lines.
1257 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001258 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001259 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1260 {
1261 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1262 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001263 // Move this entry from used list to free list
Bram Moolenaar071d4272004-06-13 20:20:40 +00001264 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001265 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001266 p = prev;
1267 retval = TRUE;
1268 }
1269 }
1270 return retval;
1271}
1272
1273/*
1274 * Free the allocated memory for a syn_state item.
1275 * Move the entry into the free list.
1276 */
1277 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001278syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001279{
1280 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001281 p->sst_next = block->b_sst_firstfree;
1282 block->b_sst_firstfree = p;
1283 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001284}
1285
1286/*
1287 * Find an entry in the list of state stacks at or before "lnum".
1288 * Returns NULL when there is no entry or the first entry is after "lnum".
1289 */
1290 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001291syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001292{
1293 synstate_T *p, *prev;
1294
1295 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001296 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001297 {
1298 if (p->sst_lnum == lnum)
1299 return p;
1300 if (p->sst_lnum > lnum)
1301 break;
1302 }
1303 return prev;
1304}
1305
1306/*
1307 * Try saving the current state in b_sst_array[].
1308 * The current state must be valid for the start of the current_lnum line!
1309 */
1310 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001311store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001312{
1313 int i;
1314 synstate_T *p;
1315 bufstate_T *bp;
1316 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001317 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001318
1319 /*
1320 * If the current state contains a start or end pattern that continues
1321 * from the previous line, we can't use it. Don't store it then.
1322 */
1323 for (i = current_state.ga_len - 1; i >= 0; --i)
1324 {
1325 cur_si = &CUR_STATE(i);
1326 if (cur_si->si_h_startpos.lnum >= current_lnum
1327 || cur_si->si_m_endpos.lnum >= current_lnum
1328 || cur_si->si_h_endpos.lnum >= current_lnum
1329 || (cur_si->si_end_idx
1330 && cur_si->si_eoe_pos.lnum >= current_lnum))
1331 break;
1332 }
1333 if (i >= 0)
1334 {
1335 if (sp != NULL)
1336 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001337 // find "sp" in the list and remove it
Bram Moolenaar860cae12010-06-05 23:22:07 +02001338 if (syn_block->b_sst_first == sp)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001339 // it's the first entry
Bram Moolenaar860cae12010-06-05 23:22:07 +02001340 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001341 else
1342 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001343 // find the entry just before this one to adjust sst_next
Bram Moolenaar00d253e2020-04-06 22:13:01 +02001344 FOR_ALL_SYNSTATES(syn_block, p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001345 if (p->sst_next == sp)
1346 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001347 if (p != NULL) // just in case
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001348 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001349 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001350 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001351 sp = NULL;
1352 }
1353 }
1354 else if (sp == NULL || sp->sst_lnum != current_lnum)
1355 {
1356 /*
1357 * Add a new entry
1358 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001359 // If no free items, cleanup the array first.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001360 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001361 {
1362 (void)syn_stack_cleanup();
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001363 // "sp" may have been moved to the freelist now
Bram Moolenaar071d4272004-06-13 20:20:40 +00001364 sp = syn_stack_find_entry(current_lnum);
1365 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001366 // Still no free items? Must be a strange problem...
Bram Moolenaar860cae12010-06-05 23:22:07 +02001367 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001368 sp = NULL;
1369 else
1370 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001371 // Take the first item from the free list and put it in the used
1372 // list, after *sp
Bram Moolenaar860cae12010-06-05 23:22:07 +02001373 p = syn_block->b_sst_firstfree;
1374 syn_block->b_sst_firstfree = p->sst_next;
1375 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001376 if (sp == NULL)
1377 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001378 // Insert in front of the list
Bram Moolenaar860cae12010-06-05 23:22:07 +02001379 p->sst_next = syn_block->b_sst_first;
1380 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001381 }
1382 else
1383 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001384 // insert in list after *sp
Bram Moolenaar071d4272004-06-13 20:20:40 +00001385 p->sst_next = sp->sst_next;
1386 sp->sst_next = p;
1387 }
1388 sp = p;
1389 sp->sst_stacksize = 0;
1390 sp->sst_lnum = current_lnum;
1391 }
1392 }
1393 if (sp != NULL)
1394 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001395 // When overwriting an existing state stack, clear it first
Bram Moolenaar071d4272004-06-13 20:20:40 +00001396 clear_syn_state(sp);
1397 sp->sst_stacksize = current_state.ga_len;
1398 if (current_state.ga_len > SST_FIX_STATES)
1399 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001400 // Need to clear it, might be something remaining from when the
1401 // length was less than SST_FIX_STATES.
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001402 ga_init2(&sp->sst_union.sst_ga, sizeof(bufstate_T), 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001403 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1404 sp->sst_stacksize = 0;
1405 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001406 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001407 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1408 }
1409 else
1410 bp = sp->sst_union.sst_stack;
1411 for (i = 0; i < sp->sst_stacksize; ++i)
1412 {
1413 bp[i].bs_idx = CUR_STATE(i).si_idx;
1414 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001415#ifdef FEAT_CONCEAL
1416 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1417 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1418#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001419 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1420 }
1421 sp->sst_next_flags = current_next_flags;
1422 sp->sst_next_list = current_next_list;
1423 sp->sst_tick = display_tick;
1424 sp->sst_change_lnum = 0;
1425 }
1426 current_state_stored = TRUE;
1427 return sp;
1428}
1429
1430/*
1431 * Copy a state stack from "from" in b_sst_array[] to current_state;
1432 */
1433 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001434load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001435{
1436 int i;
1437 bufstate_T *bp;
1438
1439 clear_current_state();
1440 validate_current_state();
1441 keepend_level = -1;
1442 if (from->sst_stacksize
1443 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1444 {
1445 if (from->sst_stacksize > SST_FIX_STATES)
1446 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1447 else
1448 bp = from->sst_union.sst_stack;
1449 for (i = 0; i < from->sst_stacksize; ++i)
1450 {
1451 CUR_STATE(i).si_idx = bp[i].bs_idx;
1452 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001453#ifdef FEAT_CONCEAL
1454 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1455 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1456#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001457 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1458 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1459 keepend_level = i;
1460 CUR_STATE(i).si_ends = FALSE;
1461 CUR_STATE(i).si_m_lnum = 0;
1462 if (CUR_STATE(i).si_idx >= 0)
1463 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001464 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001465 else
1466 CUR_STATE(i).si_next_list = NULL;
1467 update_si_attr(i);
1468 }
1469 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001470 }
1471 current_next_list = from->sst_next_list;
1472 current_next_flags = from->sst_next_flags;
1473 current_lnum = from->sst_lnum;
1474}
1475
1476/*
1477 * Compare saved state stack "*sp" with the current state.
1478 * Return TRUE when they are equal.
1479 */
1480 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001481syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001482{
1483 int i, j;
1484 bufstate_T *bp;
1485 reg_extmatch_T *six, *bsx;
1486
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001487 // First a quick check if the stacks have the same size end nextlist.
zeertzjq101d57b2022-07-31 18:34:32 +01001488 if (sp->sst_stacksize != current_state.ga_len
1489 || sp->sst_next_list != current_next_list)
1490 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001491
zeertzjq101d57b2022-07-31 18:34:32 +01001492 // Need to compare all states on both stacks.
1493 if (sp->sst_stacksize > SST_FIX_STATES)
1494 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1495 else
1496 bp = sp->sst_union.sst_stack;
1497
1498 for (i = current_state.ga_len; --i >= 0; )
1499 {
1500 // If the item has another index the state is different.
1501 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1502 break;
1503 if (bp[i].bs_extmatch == CUR_STATE(i).si_extmatch)
1504 continue;
1505 // When the extmatch pointers are different, the strings in them can
1506 // still be the same. Check if the extmatch references are equal.
1507 bsx = bp[i].bs_extmatch;
1508 six = CUR_STATE(i).si_extmatch;
1509 // If one of the extmatch pointers is NULL the states are different.
1510 if (bsx == NULL || six == NULL)
1511 break;
1512 for (j = 0; j < NSUBEXP; ++j)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001513 {
zeertzjq101d57b2022-07-31 18:34:32 +01001514 // Check each referenced match string. They must all be equal.
1515 if (bsx->matches[j] != six->matches[j])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001516 {
zeertzjq101d57b2022-07-31 18:34:32 +01001517 // If the pointer is different it can still be the same text.
1518 // Compare the strings, ignore case when the start item has the
1519 // sp_ic flag set.
1520 if (bsx->matches[j] == NULL || six->matches[j] == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001521 break;
zeertzjq101d57b2022-07-31 18:34:32 +01001522 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
1523 ? MB_STRICMP(bsx->matches[j], six->matches[j]) != 0
1524 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001525 break;
1526 }
1527 }
zeertzjq101d57b2022-07-31 18:34:32 +01001528 if (j != NSUBEXP)
1529 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001530 }
zeertzjq101d57b2022-07-31 18:34:32 +01001531 return i < 0 ? TRUE : FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001532}
1533
1534/*
1535 * We stop parsing syntax above line "lnum". If the stored state at or below
1536 * this line depended on a change before it, it now depends on the line below
1537 * the last parsed line.
1538 * The window looks like this:
1539 * line which changed
1540 * displayed line
1541 * displayed line
1542 * lnum -> line below window
1543 */
1544 void
Bram Moolenaar0abd6cf2022-10-14 17:04:09 +01001545syntax_end_parsing(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001546{
1547 synstate_T *sp;
1548
Bram Moolenaar0abd6cf2022-10-14 17:04:09 +01001549 if (syn_block != wp->w_s)
1550 return; // not the right window
Bram Moolenaar071d4272004-06-13 20:20:40 +00001551 sp = syn_stack_find_entry(lnum);
1552 if (sp != NULL && sp->sst_lnum < lnum)
1553 sp = sp->sst_next;
1554
1555 if (sp != NULL && sp->sst_change_lnum != 0)
1556 sp->sst_change_lnum = lnum;
1557}
1558
1559/*
1560 * End of handling of the state stack.
1561 ****************************************/
1562
1563 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001564invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001565{
1566 clear_current_state();
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001567 current_state.ga_itemsize = 0; // mark current_state invalid
Bram Moolenaar071d4272004-06-13 20:20:40 +00001568 current_next_list = NULL;
1569 keepend_level = -1;
1570}
1571
1572 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001573validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001574{
1575 current_state.ga_itemsize = sizeof(stateitem_T);
1576 current_state.ga_growsize = 3;
1577}
1578
1579/*
1580 * Return TRUE if the syntax at start of lnum changed since last time.
1581 * This will only be called just after get_syntax_attr() for the previous
1582 * line, to check if the next line needs to be redrawn too.
1583 */
1584 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001585syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001586{
1587 int retval = TRUE;
1588 synstate_T *sp;
1589
Bram Moolenaar071d4272004-06-13 20:20:40 +00001590 /*
1591 * Check the state stack when:
1592 * - lnum is just below the previously syntaxed line.
1593 * - lnum is not before the lines with saved states.
1594 * - lnum is not past the lines with saved states.
1595 * - lnum is at or before the last changed line.
1596 */
1597 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1598 {
1599 sp = syn_stack_find_entry(lnum);
1600 if (sp != NULL && sp->sst_lnum == lnum)
1601 {
1602 /*
1603 * finish the previous line (needed when not all of the line was
1604 * drawn)
1605 */
1606 (void)syn_finish_line(FALSE);
1607
1608 /*
1609 * Compare the current state with the previously saved state of
1610 * the line.
1611 */
1612 if (syn_stack_equal(sp))
1613 retval = FALSE;
1614
1615 /*
1616 * Store the current state in b_sst_array[] for later use.
1617 */
1618 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001619 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001620 }
1621 }
1622
Bram Moolenaar071d4272004-06-13 20:20:40 +00001623 return retval;
1624}
1625
1626/*
1627 * Finish the current line.
1628 * This doesn't return any attributes, it only gets the state at the end of
1629 * the line. It can start anywhere in the line, as long as the current state
1630 * is valid.
1631 */
1632 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001633syn_finish_line(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001634 int syncing) // called for syncing
Bram Moolenaar071d4272004-06-13 20:20:40 +00001635{
1636 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001637 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001638
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001639 while (!current_finished)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001640 {
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001641 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1642 /*
1643 * When syncing, and found some item, need to check the item.
1644 */
1645 if (syncing && current_state.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001646 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001647 /*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001648 * Check for match with sync item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001649 */
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001650 cur_si = &CUR_STATE(current_state.ga_len - 1);
1651 if (cur_si->si_idx >= 0
1652 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1653 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1654 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001655
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001656 // syn_current_attr() will have skipped the check for an item
1657 // that ends here, need to do that now. Be careful not to go
1658 // past the NUL.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001659 prev_current_col = current_col;
1660 if (syn_getcurline()[current_col] != NUL)
1661 ++current_col;
1662 check_state_ends();
1663 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001664 }
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001665 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001666 }
1667 return FALSE;
1668}
1669
1670/*
1671 * Return highlight attributes for next character.
1672 * Must first call syntax_start() once for the line.
1673 * "col" is normally 0 for the first use in a line, and increments by one each
1674 * time. It's allowed to skip characters and to stop before the end of the
1675 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001676 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1677 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001678 */
1679 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001680get_syntax_attr(
1681 colnr_T col,
1682 int *can_spell,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001683 int keep_state) // keep state of char at "col"
Bram Moolenaar071d4272004-06-13 20:20:40 +00001684{
1685 int attr = 0;
1686
Bram Moolenaar349955a2007-08-14 21:07:36 +00001687 if (can_spell != NULL)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001688 // Default: Only do spelling when there is no @Spell cluster or when
1689 // ":syn spell toplevel" was used.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001690 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1691 ? (syn_block->b_spell_cluster_id == 0)
1692 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001693
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001694 // check for out of memory situation
Bram Moolenaar860cae12010-06-05 23:22:07 +02001695 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001696 return 0;
1697
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001698 // After 'synmaxcol' the attribute is always zero.
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001699 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001700 {
1701 clear_current_state();
1702#ifdef FEAT_EVAL
1703 current_id = 0;
1704 current_trans_id = 0;
1705#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001706#ifdef FEAT_CONCEAL
1707 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001708 current_seqnr = 0;
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001709#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001710 return 0;
1711 }
1712
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001713 // Make sure current_state is valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00001714 if (INVALID_STATE(&current_state))
1715 validate_current_state();
1716
1717 /*
1718 * Skip from the current column to "col", get the attributes for "col".
1719 */
1720 while (current_col <= col)
1721 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001722 attr = syn_current_attr(FALSE, TRUE, can_spell,
1723 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001724 ++current_col;
1725 }
1726
Bram Moolenaar071d4272004-06-13 20:20:40 +00001727 return attr;
1728}
1729
1730/*
1731 * Get syntax attributes for current_lnum, current_col.
1732 */
1733 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001734syn_current_attr(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001735 int syncing, // When 1: called for syncing
1736 int displaying, // result will be displayed
1737 int *can_spell, // return: do spell checking
1738 int keep_state) // keep syntax stack afterwards
Bram Moolenaar071d4272004-06-13 20:20:40 +00001739{
1740 int syn_id;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001741 lpos_T endpos; // was: char_u *endp;
1742 lpos_T hl_startpos; // was: int hl_startcol;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001743 lpos_T hl_endpos;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001744 lpos_T eos_pos; // end-of-start match (start region)
1745 lpos_T eoe_pos; // end-of-end pattern
1746 int end_idx; // group ID for end pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00001747 int idx;
1748 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001749 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001750 int startcol;
1751 int endcol;
1752 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001753 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001754 short *next_list;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001755 int found_match; // found usable match
1756 static int try_next_column = FALSE; // must try in next col
Bram Moolenaar071d4272004-06-13 20:20:40 +00001757 int do_keywords;
1758 regmmatch_T regmatch;
1759 lpos_T pos;
1760 int lc_col;
1761 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001762 char_u buf_chartab[32]; // chartab array for syn iskyeyword
1763 char_u *line; // current line. NOTE: becomes invalid after
1764 // looking for a pattern match!
Bram Moolenaar071d4272004-06-13 20:20:40 +00001765
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001766 // variables for zero-width matches that have a "nextgroup" argument
Bram Moolenaar071d4272004-06-13 20:20:40 +00001767 int keep_next_list;
1768 int zero_width_next_list = FALSE;
1769 garray_T zero_width_next_ga;
1770
1771 /*
1772 * No character, no attributes! Past end of line?
1773 * Do try matching with an empty line (could be the start of a region).
1774 */
1775 line = syn_getcurline();
1776 if (line[current_col] == NUL && current_col != 0)
1777 {
1778 /*
1779 * If we found a match after the last column, use it.
1780 */
1781 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1782 && next_match_col != MAXCOL)
1783 (void)push_next_match(NULL);
1784
1785 current_finished = TRUE;
1786 current_state_stored = FALSE;
1787 return 0;
1788 }
1789
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001790 // if the current or next character is NUL, we will finish the line now
Bram Moolenaar071d4272004-06-13 20:20:40 +00001791 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1792 {
1793 current_finished = TRUE;
1794 current_state_stored = FALSE;
1795 }
1796
1797 /*
1798 * When in the previous column there was a match but it could not be used
1799 * (empty match or already matched in this column) need to try again in
1800 * the next column.
1801 */
1802 if (try_next_column)
1803 {
1804 next_match_idx = -1;
1805 try_next_column = FALSE;
1806 }
1807
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001808 // Only check for keywords when not syncing and there are some.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001809 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001810 && (syn_block->b_keywtab.ht_used > 0
1811 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001812
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001813 // Init the list of zero-width matches with a nextlist. This is used to
1814 // avoid matching the same item in the same position twice.
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001815 ga_init2(&zero_width_next_ga, sizeof(int), 10);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001816
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001817 // use syntax iskeyword option
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001818 save_chartab(buf_chartab);
1819
Bram Moolenaar071d4272004-06-13 20:20:40 +00001820 /*
1821 * Repeat matching keywords and patterns, to find contained items at the
1822 * same column. This stops when there are no extra matches at the current
1823 * column.
1824 */
1825 do
1826 {
1827 found_match = FALSE;
1828 keep_next_list = FALSE;
1829 syn_id = 0;
1830
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001831
Bram Moolenaar071d4272004-06-13 20:20:40 +00001832 /*
1833 * 1. Check for a current state.
1834 * Only when there is no current state, or if the current state may
1835 * contain other things, we need to check for keywords and patterns.
1836 * Always need to check for contained items if some item has the
1837 * "containedin" argument (takes extra time!).
1838 */
1839 if (current_state.ga_len)
1840 cur_si = &CUR_STATE(current_state.ga_len - 1);
1841 else
1842 cur_si = NULL;
1843
Bram Moolenaar860cae12010-06-05 23:22:07 +02001844 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001845 || cur_si->si_cont_list != NULL)
1846 {
1847 /*
1848 * 2. Check for keywords, if on a keyword char after a non-keyword
1849 * char. Don't do this when syncing.
1850 */
1851 if (do_keywords)
1852 {
1853 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001854 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001855 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001856 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00001857 - (has_mbyte
1858 ? (*mb_head_off)(line, line + current_col - 1)
Bram Moolenaar264b74f2019-01-24 17:18:42 +01001859 : 0) , syn_buf)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001860 {
1861 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001862 &endcol, &flags, &next_list, cur_si,
1863 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001864 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001865 {
1866 if (push_current_state(KEYWORD_IDX) == OK)
1867 {
1868 cur_si = &CUR_STATE(current_state.ga_len - 1);
1869 cur_si->si_m_startcol = current_col;
1870 cur_si->si_h_startpos.lnum = current_lnum;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001871 cur_si->si_h_startpos.col = 0; // starts right away
Bram Moolenaar071d4272004-06-13 20:20:40 +00001872 cur_si->si_m_endpos.lnum = current_lnum;
1873 cur_si->si_m_endpos.col = endcol;
1874 cur_si->si_h_endpos.lnum = current_lnum;
1875 cur_si->si_h_endpos.col = endcol;
1876 cur_si->si_ends = TRUE;
1877 cur_si->si_end_idx = 0;
1878 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001879#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02001880 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001881 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001882 if (current_state.ga_len > 1)
1883 cur_si->si_flags |=
1884 CUR_STATE(current_state.ga_len - 2).si_flags
1885 & HL_CONCEAL;
1886#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001887 cur_si->si_id = syn_id;
1888 cur_si->si_trans_id = syn_id;
1889 if (flags & HL_TRANSP)
1890 {
1891 if (current_state.ga_len < 2)
1892 {
1893 cur_si->si_attr = 0;
1894 cur_si->si_trans_id = 0;
1895 }
1896 else
1897 {
1898 cur_si->si_attr = CUR_STATE(
1899 current_state.ga_len - 2).si_attr;
1900 cur_si->si_trans_id = CUR_STATE(
1901 current_state.ga_len - 2).si_trans_id;
1902 }
1903 }
1904 else
1905 cur_si->si_attr = syn_id2attr(syn_id);
1906 cur_si->si_cont_list = NULL;
1907 cur_si->si_next_list = next_list;
1908 check_keepend();
1909 }
1910 else
1911 vim_free(next_list);
1912 }
1913 }
1914 }
1915
1916 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001917 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001918 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001919 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001920 {
1921 /*
1922 * If we didn't check for a match yet, or we are past it, check
1923 * for any match with a pattern.
1924 */
1925 if (next_match_idx < 0 || next_match_col < (int)current_col)
1926 {
1927 /*
1928 * Check all relevant patterns for a match at this
1929 * position. This is complicated, because matching with a
1930 * pattern takes quite a bit of time, thus we want to
1931 * avoid doing it when it's not needed.
1932 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001933 next_match_idx = 0; // no match in this line yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00001934 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001935 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001936 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001937 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001938 if ( spp->sp_syncing == syncing
1939 && (displaying || !(spp->sp_flags & HL_DISPLAY))
1940 && (spp->sp_type == SPTYPE_MATCH
1941 || spp->sp_type == SPTYPE_START)
1942 && (current_next_list != NULL
1943 ? in_id_list(NULL, current_next_list,
1944 &spp->sp_syn, 0)
1945 : (cur_si == NULL
1946 ? !(spp->sp_flags & HL_CONTAINED)
1947 : in_id_list(cur_si,
1948 cur_si->si_cont_list, &spp->sp_syn,
1949 spp->sp_flags & HL_CONTAINED))))
1950 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001951 int r;
1952
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001953 // If we already tried matching in this line, and
1954 // there isn't a match before next_match_col, skip
1955 // this item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001956 if (spp->sp_line_id == current_line_id
1957 && spp->sp_startcol >= next_match_col)
1958 continue;
1959 spp->sp_line_id = current_line_id;
1960
1961 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
1962 if (lc_col < 0)
1963 lc_col = 0;
1964
1965 regmatch.rmm_ic = spp->sp_ic;
1966 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001967 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001968 current_lnum,
1969 (colnr_T)lc_col,
Bram Moolenaard23a8232018-02-10 18:45:26 +01001970 IF_SYN_TIME(&spp->sp_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001971 spp->sp_prog = regmatch.regprog;
1972 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001973 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001974 // no match in this line, try another one
Bram Moolenaar071d4272004-06-13 20:20:40 +00001975 spp->sp_startcol = MAXCOL;
1976 continue;
1977 }
1978
1979 /*
1980 * Compute the first column of the match.
1981 */
1982 syn_add_start_off(&pos, &regmatch,
1983 spp, SPO_MS_OFF, -1);
1984 if (pos.lnum > current_lnum)
1985 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001986 // must have used end of match in a next line,
1987 // we can't handle that
Bram Moolenaar071d4272004-06-13 20:20:40 +00001988 spp->sp_startcol = MAXCOL;
1989 continue;
1990 }
1991 startcol = pos.col;
1992
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001993 // remember the next column where this pattern
1994 // matches in the current line
Bram Moolenaar071d4272004-06-13 20:20:40 +00001995 spp->sp_startcol = startcol;
1996
1997 /*
1998 * If a previously found match starts at a lower
1999 * column number, don't use this one.
2000 */
2001 if (startcol >= next_match_col)
2002 continue;
2003
2004 /*
2005 * If we matched this pattern at this position
2006 * before, skip it. Must retry in the next
2007 * column, because it may match from there.
2008 */
2009 if (did_match_already(idx, &zero_width_next_ga))
2010 {
2011 try_next_column = TRUE;
2012 continue;
2013 }
2014
2015 endpos.lnum = regmatch.endpos[0].lnum;
2016 endpos.col = regmatch.endpos[0].col;
2017
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002018 // Compute the highlight start.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002019 syn_add_start_off(&hl_startpos, &regmatch,
2020 spp, SPO_HS_OFF, -1);
2021
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002022 // Compute the region start.
2023 // Default is to use the end of the match.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002024 syn_add_end_off(&eos_pos, &regmatch,
2025 spp, SPO_RS_OFF, 0);
2026
2027 /*
2028 * Grab the external submatches before they get
2029 * overwritten. Reference count doesn't change.
2030 */
2031 unref_extmatch(cur_extmatch);
2032 cur_extmatch = re_extmatch_out;
2033 re_extmatch_out = NULL;
2034
2035 flags = 0;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002036 eoe_pos.lnum = 0; // avoid warning
Bram Moolenaar071d4272004-06-13 20:20:40 +00002037 eoe_pos.col = 0;
2038 end_idx = 0;
2039 hl_endpos.lnum = 0;
2040
2041 /*
2042 * For a "oneline" the end must be found in the
2043 * same line too. Search for it after the end of
2044 * the match with the start pattern. Set the
2045 * resulting end positions at the same time.
2046 */
2047 if (spp->sp_type == SPTYPE_START
2048 && (spp->sp_flags & HL_ONELINE))
2049 {
2050 lpos_T startpos;
2051
2052 startpos = endpos;
2053 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2054 &flags, &eoe_pos, &end_idx, cur_extmatch);
2055 if (endpos.lnum == 0)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002056 continue; // not found
Bram Moolenaar071d4272004-06-13 20:20:40 +00002057 }
2058
2059 /*
2060 * For a "match" the size must be > 0 after the
2061 * end offset needs has been added. Except when
2062 * syncing.
2063 */
2064 else if (spp->sp_type == SPTYPE_MATCH)
2065 {
2066 syn_add_end_off(&hl_endpos, &regmatch, spp,
2067 SPO_HE_OFF, 0);
2068 syn_add_end_off(&endpos, &regmatch, spp,
2069 SPO_ME_OFF, 0);
2070 if (endpos.lnum == current_lnum
2071 && (int)endpos.col + syncing < startcol)
2072 {
2073 /*
2074 * If an empty string is matched, may need
2075 * to try matching again at next column.
2076 */
2077 if (regmatch.startpos[0].col
2078 == regmatch.endpos[0].col)
2079 try_next_column = TRUE;
2080 continue;
2081 }
2082 }
2083
2084 /*
2085 * keep the best match so far in next_match_*
2086 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002087 // Highlighting must start after startpos and end
2088 // before endpos.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002089 if (hl_startpos.lnum == current_lnum
2090 && (int)hl_startpos.col < startcol)
2091 hl_startpos.col = startcol;
2092 limit_pos_zero(&hl_endpos, &endpos);
2093
2094 next_match_idx = idx;
2095 next_match_col = startcol;
2096 next_match_m_endpos = endpos;
2097 next_match_h_endpos = hl_endpos;
2098 next_match_h_startpos = hl_startpos;
2099 next_match_flags = flags;
2100 next_match_eos_pos = eos_pos;
2101 next_match_eoe_pos = eoe_pos;
2102 next_match_end_idx = end_idx;
2103 unref_extmatch(next_match_extmatch);
2104 next_match_extmatch = cur_extmatch;
2105 cur_extmatch = NULL;
2106 }
2107 }
2108 }
2109
2110 /*
2111 * If we found a match at the current column, use it.
2112 */
2113 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2114 {
2115 synpat_T *lspp;
2116
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002117 // When a zero-width item matched which has a nextgroup,
2118 // don't push the item but set nextgroup.
Bram Moolenaar860cae12010-06-05 23:22:07 +02002119 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002120 if (next_match_m_endpos.lnum == current_lnum
2121 && next_match_m_endpos.col == current_col
2122 && lspp->sp_next_list != NULL)
2123 {
2124 current_next_list = lspp->sp_next_list;
2125 current_next_flags = lspp->sp_flags;
2126 keep_next_list = TRUE;
2127 zero_width_next_list = TRUE;
2128
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002129 // Add the index to a list, so that we can check
2130 // later that we don't match it again (and cause an
2131 // endless loop).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002132 if (ga_grow(&zero_width_next_ga, 1) == OK)
2133 {
2134 ((int *)(zero_width_next_ga.ga_data))
2135 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002136 }
2137 next_match_idx = -1;
2138 }
2139 else
2140 cur_si = push_next_match(cur_si);
2141 found_match = TRUE;
2142 }
2143 }
2144 }
2145
2146 /*
2147 * Handle searching for nextgroup match.
2148 */
2149 if (current_next_list != NULL && !keep_next_list)
2150 {
2151 /*
2152 * If a nextgroup was not found, continue looking for one if:
2153 * - this is an empty line and the "skipempty" option was given
2154 * - we are on white space and the "skipwhite" option was given
2155 */
2156 if (!found_match)
2157 {
2158 line = syn_getcurline();
2159 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002160 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002161 || ((current_next_flags & HL_SKIPEMPTY)
2162 && *line == NUL))
2163 break;
2164 }
2165
2166 /*
2167 * If a nextgroup was found: Use it, and continue looking for
2168 * contained matches.
2169 * If a nextgroup was not found: Continue looking for a normal
2170 * match.
2171 * When did set current_next_list for a zero-width item and no
2172 * match was found don't loop (would get stuck).
2173 */
2174 current_next_list = NULL;
2175 next_match_idx = -1;
2176 if (!zero_width_next_list)
2177 found_match = TRUE;
2178 }
2179
2180 } while (found_match);
2181
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002182 restore_chartab(buf_chartab);
2183
Bram Moolenaar071d4272004-06-13 20:20:40 +00002184 /*
2185 * Use attributes from the current state, if within its highlighting.
2186 * If not, use attributes from the current-but-one state, etc.
2187 */
2188 current_attr = 0;
2189#ifdef FEAT_EVAL
2190 current_id = 0;
2191 current_trans_id = 0;
2192#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002193#ifdef FEAT_CONCEAL
2194 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02002195 current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002196#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002197 if (cur_si != NULL)
2198 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002199#ifndef FEAT_EVAL
2200 int current_trans_id = 0;
2201#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002202 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2203 {
2204 sip = &CUR_STATE(idx);
2205 if ((current_lnum > sip->si_h_startpos.lnum
2206 || (current_lnum == sip->si_h_startpos.lnum
2207 && current_col >= sip->si_h_startpos.col))
2208 && (sip->si_h_endpos.lnum == 0
2209 || current_lnum < sip->si_h_endpos.lnum
2210 || (current_lnum == sip->si_h_endpos.lnum
2211 && current_col < sip->si_h_endpos.col)))
2212 {
2213 current_attr = sip->si_attr;
2214#ifdef FEAT_EVAL
2215 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002216#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002217 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002218#ifdef FEAT_CONCEAL
2219 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002220 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002221 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002222#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002223 break;
2224 }
2225 }
2226
Bram Moolenaar217ad922005-03-20 22:37:15 +00002227 if (can_spell != NULL)
2228 {
2229 struct sp_syn sps;
2230
2231 /*
2232 * set "can_spell" to TRUE if spell checking is supposed to be
2233 * done in the current item.
2234 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002235 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002236 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002237 // There is no @Spell cluster: Do spelling for items without
2238 // @NoSpell cluster.
Bram Moolenaar860cae12010-06-05 23:22:07 +02002239 if (syn_block->b_nospell_cluster_id == 0
2240 || current_trans_id == 0)
2241 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002242 else
2243 {
2244 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002245 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002246 sps.cont_in_list = NULL;
2247 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2248 }
2249 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002250 else
2251 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002252 // The @Spell cluster is defined: Do spelling in items with
2253 // the @Spell cluster. But not when @NoSpell is also there.
2254 // At the toplevel only spell check when ":syn spell toplevel"
2255 // was used.
Bram Moolenaar3638c682005-06-08 22:05:14 +00002256 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002257 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002258 else
2259 {
2260 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002261 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002262 sps.cont_in_list = NULL;
2263 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2264
Bram Moolenaar860cae12010-06-05 23:22:07 +02002265 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002266 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002267 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002268 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2269 *can_spell = FALSE;
2270 }
2271 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002272 }
2273 }
2274
2275
Bram Moolenaar071d4272004-06-13 20:20:40 +00002276 /*
2277 * Check for end of current state (and the states before it) at the
2278 * next column. Don't do this for syncing, because we would miss a
2279 * single character match.
2280 * First check if the current state ends at the current column. It
2281 * may be for an empty match and a containing item might end in the
2282 * current column.
2283 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002284 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002285 {
2286 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002287 if (current_state.ga_len > 0
2288 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002289 {
2290 ++current_col;
2291 check_state_ends();
2292 --current_col;
2293 }
2294 }
2295 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002296 else if (can_spell != NULL)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002297 // Default: Only do spelling when there is no @Spell cluster or when
2298 // ":syn spell toplevel" was used.
Bram Moolenaar860cae12010-06-05 23:22:07 +02002299 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2300 ? (syn_block->b_spell_cluster_id == 0)
2301 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002302
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002303 // nextgroup ends at end of line, unless "skipnl" or "skipempty" present
Bram Moolenaar071d4272004-06-13 20:20:40 +00002304 if (current_next_list != NULL
Bram Moolenaar069dafc2018-03-03 20:02:19 +01002305 && (line = syn_getcurline())[current_col] != NUL
2306 && line[current_col + 1] == NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002307 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2308 current_next_list = NULL;
2309
2310 if (zero_width_next_ga.ga_len > 0)
2311 ga_clear(&zero_width_next_ga);
2312
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002313 // No longer need external matches. But keep next_match_extmatch.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002314 unref_extmatch(re_extmatch_out);
2315 re_extmatch_out = NULL;
2316 unref_extmatch(cur_extmatch);
2317
2318 return current_attr;
2319}
2320
2321
2322/*
2323 * Check if we already matched pattern "idx" at the current column.
2324 */
2325 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002326did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002327{
2328 int i;
2329
2330 for (i = current_state.ga_len; --i >= 0; )
2331 if (CUR_STATE(i).si_m_startcol == (int)current_col
2332 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2333 && CUR_STATE(i).si_idx == idx)
2334 return TRUE;
2335
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002336 // Zero-width matches with a nextgroup argument are not put on the syntax
2337 // stack, and can only be matched once anyway.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002338 for (i = gap->ga_len; --i >= 0; )
2339 if (((int *)(gap->ga_data))[i] == idx)
2340 return TRUE;
2341
2342 return FALSE;
2343}
2344
2345/*
2346 * Push the next match onto the stack.
2347 */
2348 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002349push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002350{
2351 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002352#ifdef FEAT_CONCEAL
2353 int save_flags;
2354#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002355
Bram Moolenaar860cae12010-06-05 23:22:07 +02002356 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002357
2358 /*
2359 * Push the item in current_state stack;
2360 */
2361 if (push_current_state(next_match_idx) == OK)
2362 {
2363 /*
2364 * If it's a start-skip-end type that crosses lines, figure out how
2365 * much it continues in this line. Otherwise just fill in the length.
2366 */
2367 cur_si = &CUR_STATE(current_state.ga_len - 1);
2368 cur_si->si_h_startpos = next_match_h_startpos;
2369 cur_si->si_m_startcol = current_col;
2370 cur_si->si_m_lnum = current_lnum;
2371 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002372#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002373 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002374 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002375 if (current_state.ga_len > 1)
2376 cur_si->si_flags |=
2377 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2378#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002379 cur_si->si_next_list = spp->sp_next_list;
2380 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2381 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2382 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002383 // Try to find the end pattern in the current line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002384 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2385 check_keepend();
2386 }
2387 else
2388 {
2389 cur_si->si_m_endpos = next_match_m_endpos;
2390 cur_si->si_h_endpos = next_match_h_endpos;
2391 cur_si->si_ends = TRUE;
2392 cur_si->si_flags |= next_match_flags;
2393 cur_si->si_eoe_pos = next_match_eoe_pos;
2394 cur_si->si_end_idx = next_match_end_idx;
2395 }
2396 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2397 keepend_level = current_state.ga_len - 1;
2398 check_keepend();
2399 update_si_attr(current_state.ga_len - 1);
2400
Bram Moolenaar860cae12010-06-05 23:22:07 +02002401#ifdef FEAT_CONCEAL
2402 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2403#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002404 /*
2405 * If the start pattern has another highlight group, push another item
2406 * on the stack for the start pattern.
2407 */
2408 if ( spp->sp_type == SPTYPE_START
2409 && spp->sp_syn_match_id != 0
2410 && push_current_state(next_match_idx) == OK)
2411 {
2412 cur_si = &CUR_STATE(current_state.ga_len - 1);
2413 cur_si->si_h_startpos = next_match_h_startpos;
2414 cur_si->si_m_startcol = current_col;
2415 cur_si->si_m_lnum = current_lnum;
2416 cur_si->si_m_endpos = next_match_eos_pos;
2417 cur_si->si_h_endpos = next_match_eos_pos;
2418 cur_si->si_ends = TRUE;
2419 cur_si->si_end_idx = 0;
2420 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002421#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002422 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002423 cur_si->si_flags |= save_flags;
2424 if (cur_si->si_flags & HL_CONCEALENDS)
2425 cur_si->si_flags |= HL_CONCEAL;
2426#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002427 cur_si->si_next_list = NULL;
2428 check_keepend();
2429 update_si_attr(current_state.ga_len - 1);
2430 }
2431 }
2432
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002433 next_match_idx = -1; // try other match next time
Bram Moolenaar071d4272004-06-13 20:20:40 +00002434
2435 return cur_si;
2436}
2437
2438/*
2439 * Check for end of current state (and the states before it).
2440 */
2441 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002442check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002443{
2444 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002445 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002446
2447 cur_si = &CUR_STATE(current_state.ga_len - 1);
2448 for (;;)
2449 {
2450 if (cur_si->si_ends
2451 && (cur_si->si_m_endpos.lnum < current_lnum
2452 || (cur_si->si_m_endpos.lnum == current_lnum
2453 && cur_si->si_m_endpos.col <= current_col)))
2454 {
2455 /*
2456 * If there is an end pattern group ID, highlight the end pattern
2457 * now. No need to pop the current item from the stack.
2458 * Only do this if the end pattern continues beyond the current
2459 * position.
2460 */
2461 if (cur_si->si_end_idx
2462 && (cur_si->si_eoe_pos.lnum > current_lnum
2463 || (cur_si->si_eoe_pos.lnum == current_lnum
2464 && cur_si->si_eoe_pos.col > current_col)))
2465 {
2466 cur_si->si_idx = cur_si->si_end_idx;
2467 cur_si->si_end_idx = 0;
2468 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2469 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2470 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002471#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002472 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002473 if (cur_si->si_flags & HL_CONCEALENDS)
2474 cur_si->si_flags |= HL_CONCEAL;
2475#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002476 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002477
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002478 // nextgroup= should not match in the end pattern
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002479 current_next_list = NULL;
2480
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002481 // what matches next may be different now, clear it
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002482 next_match_idx = 0;
2483 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002484 break;
2485 }
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002486
2487 // handle next_list, unless at end of line and no "skipnl" or
2488 // "skipempty"
2489 current_next_list = cur_si->si_next_list;
2490 current_next_flags = cur_si->si_flags;
2491 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2492 && syn_getcurline()[current_col] == NUL)
2493 current_next_list = NULL;
2494
2495 // When the ended item has "extend", another item with
2496 // "keepend" now needs to check for its end.
2497 had_extend = (cur_si->si_flags & HL_EXTEND);
2498
2499 pop_current_state();
2500
2501 if (current_state.ga_len == 0)
2502 break;
2503
2504 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002505 {
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002506 syn_update_ends(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002507 if (current_state.ga_len == 0)
2508 break;
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002509 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002510
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002511 cur_si = &CUR_STATE(current_state.ga_len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002512
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002513 /*
2514 * Only for a region the search for the end continues after
2515 * the end of the contained item. If the contained match
2516 * included the end-of-line, break here, the region continues.
2517 * Don't do this when:
2518 * - "keepend" is used for the contained item
2519 * - not at the end of the line (could be end="x$"me=e-1).
2520 * - "excludenl" is used (HL_HAS_EOL won't be set)
2521 */
2522 if (cur_si->si_idx >= 0
2523 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
2524 == SPTYPE_START
2525 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2526 {
2527 update_si_end(cur_si, (int)current_col, TRUE);
2528 check_keepend();
2529 if ((current_next_flags & HL_HAS_EOL)
2530 && keepend_level < 0
2531 && syn_getcurline()[current_col] == NUL)
2532 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002533 }
2534 }
2535 else
2536 break;
2537 }
2538}
2539
2540/*
2541 * Update an entry in the current_state stack for a match or region. This
2542 * fills in si_attr, si_next_list and si_cont_list.
2543 */
2544 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002545update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002546{
2547 stateitem_T *sip = &CUR_STATE(idx);
2548 synpat_T *spp;
2549
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002550 // This should not happen...
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002551 if (sip->si_idx < 0)
2552 return;
2553
Bram Moolenaar860cae12010-06-05 23:22:07 +02002554 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002555 if (sip->si_flags & HL_MATCH)
2556 sip->si_id = spp->sp_syn_match_id;
2557 else
2558 sip->si_id = spp->sp_syn.id;
2559 sip->si_attr = syn_id2attr(sip->si_id);
2560 sip->si_trans_id = sip->si_id;
2561 if (sip->si_flags & HL_MATCH)
2562 sip->si_cont_list = NULL;
2563 else
2564 sip->si_cont_list = spp->sp_cont_list;
2565
2566 /*
2567 * For transparent items, take attr from outer item.
2568 * Also take cont_list, if there is none.
2569 * Don't do this for the matchgroup of a start or end pattern.
2570 */
2571 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2572 {
2573 if (idx == 0)
2574 {
2575 sip->si_attr = 0;
2576 sip->si_trans_id = 0;
2577 if (sip->si_cont_list == NULL)
2578 sip->si_cont_list = ID_LIST_ALL;
2579 }
2580 else
2581 {
2582 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2583 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
2584 if (sip->si_cont_list == NULL)
2585 {
2586 sip->si_flags |= HL_TRANS_CONT;
2587 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2588 }
2589 }
2590 }
2591}
2592
2593/*
2594 * Check the current stack for patterns with "keepend" flag.
2595 * Propagate the match-end to contained items, until a "skipend" item is found.
2596 */
2597 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002598check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002599{
2600 int i;
2601 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002602 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002603 stateitem_T *sip;
2604
2605 /*
2606 * This check can consume a lot of time; only do it from the level where
2607 * there really is a keepend.
2608 */
2609 if (keepend_level < 0)
2610 return;
2611
2612 /*
2613 * Find the last index of an "extend" item. "keepend" items before that
2614 * won't do anything. If there is no "extend" item "i" will be
2615 * "keepend_level" and all "keepend" items will work normally.
2616 */
2617 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2618 if (CUR_STATE(i).si_flags & HL_EXTEND)
2619 break;
2620
2621 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002622 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002623 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002624 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002625 for ( ; i < current_state.ga_len; ++i)
2626 {
2627 sip = &CUR_STATE(i);
2628 if (maxpos.lnum != 0)
2629 {
2630 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002631 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002632 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2633 sip->si_ends = TRUE;
2634 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002635 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2636 {
2637 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002638 || maxpos.lnum > sip->si_m_endpos.lnum
2639 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002640 && maxpos.col > sip->si_m_endpos.col))
2641 maxpos = sip->si_m_endpos;
2642 if (maxpos_h.lnum == 0
2643 || maxpos_h.lnum > sip->si_h_endpos.lnum
2644 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2645 && maxpos_h.col > sip->si_h_endpos.col))
2646 maxpos_h = sip->si_h_endpos;
2647 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002648 }
2649}
2650
2651/*
2652 * Update an entry in the current_state stack for a start-skip-end pattern.
2653 * This finds the end of the current item, if it's in the current line.
2654 *
2655 * Return the flags for the matched END.
2656 */
2657 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002658update_si_end(
2659 stateitem_T *sip,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002660 int startcol, // where to start searching for the end
2661 int force) // when TRUE overrule a previous end
Bram Moolenaar071d4272004-06-13 20:20:40 +00002662{
2663 lpos_T startpos;
2664 lpos_T endpos;
2665 lpos_T hl_endpos;
2666 lpos_T end_endpos;
2667 int end_idx;
2668
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002669 // return quickly for a keyword
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002670 if (sip->si_idx < 0)
2671 return;
2672
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002673 // Don't update when it's already done. Can be a match of an end pattern
2674 // that started in a previous line. Watch out: can also be a "keepend"
2675 // from a containing item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002676 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2677 return;
2678
2679 /*
2680 * We need to find the end of the region. It may continue in the next
2681 * line.
2682 */
2683 end_idx = 0;
2684 startpos.lnum = current_lnum;
2685 startpos.col = startcol;
2686 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2687 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2688
2689 if (endpos.lnum == 0)
2690 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002691 // No end pattern matched.
Bram Moolenaar860cae12010-06-05 23:22:07 +02002692 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002693 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002694 // a "oneline" never continues in the next line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002695 sip->si_ends = TRUE;
2696 sip->si_m_endpos.lnum = current_lnum;
2697 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2698 }
2699 else
2700 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002701 // continues in the next line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002702 sip->si_ends = FALSE;
2703 sip->si_m_endpos.lnum = 0;
2704 }
2705 sip->si_h_endpos = sip->si_m_endpos;
2706 }
2707 else
2708 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002709 // match within this line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002710 sip->si_m_endpos = endpos;
2711 sip->si_h_endpos = hl_endpos;
2712 sip->si_eoe_pos = end_endpos;
2713 sip->si_ends = TRUE;
2714 sip->si_end_idx = end_idx;
2715 }
2716}
2717
2718/*
2719 * Add a new state to the current state stack.
2720 * It is cleared and the index set to "idx".
2721 * Return FAIL if it's not possible (out of memory).
2722 */
2723 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002724push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002725{
2726 if (ga_grow(&current_state, 1) == FAIL)
2727 return FAIL;
Bram Moolenaara80faa82020-04-12 19:37:17 +02002728 CLEAR_POINTER(&CUR_STATE(current_state.ga_len));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002729 CUR_STATE(current_state.ga_len).si_idx = idx;
2730 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002731 return OK;
2732}
2733
2734/*
2735 * Remove a state from the current_state stack.
2736 */
2737 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002738pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002739{
2740 if (current_state.ga_len)
2741 {
2742 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2743 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002744 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002745 // after the end of a pattern, try matching a keyword or pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00002746 next_match_idx = -1;
2747
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002748 // if first state with "keepend" is popped, reset keepend_level
Bram Moolenaar071d4272004-06-13 20:20:40 +00002749 if (keepend_level >= current_state.ga_len)
2750 keepend_level = -1;
2751}
2752
2753/*
2754 * Find the end of a start/skip/end syntax region after "startpos".
2755 * Only checks one line.
2756 * Also handles a match item that continued from a previous line.
2757 * If not found, the syntax item continues in the next line. m_endpos->lnum
2758 * will be 0.
2759 * If found, the end of the region and the end of the highlighting is
2760 * computed.
2761 */
2762 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002763find_endpos(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002764 int idx, // index of the pattern
2765 lpos_T *startpos, // where to start looking for an END match
2766 lpos_T *m_endpos, // return: end of match
2767 lpos_T *hl_endpos, // return: end of highlighting
2768 long *flagsp, // return: flags of matching END
2769 lpos_T *end_endpos, // return: end of end pattern match
2770 int *end_idx, // return: group ID for end pat. match, or 0
2771 reg_extmatch_T *start_ext) // submatches from the start pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00002772{
2773 colnr_T matchcol;
2774 synpat_T *spp, *spp_skip;
2775 int start_idx;
2776 int best_idx;
2777 regmmatch_T regmatch;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002778 regmmatch_T best_regmatch; // startpos/endpos of best match
Bram Moolenaar071d4272004-06-13 20:20:40 +00002779 lpos_T pos;
2780 char_u *line;
2781 int had_match = FALSE;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002782 char_u buf_chartab[32]; // chartab array for syn option iskyeyword
Bram Moolenaar071d4272004-06-13 20:20:40 +00002783
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002784 // just in case we are invoked for a keyword
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002785 if (idx < 0)
2786 return;
2787
Bram Moolenaar071d4272004-06-13 20:20:40 +00002788 /*
2789 * Check for being called with a START pattern.
2790 * Can happen with a match that continues to the next line, because it
2791 * contained a region.
2792 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002793 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002794 if (spp->sp_type != SPTYPE_START)
2795 {
2796 *hl_endpos = *startpos;
2797 return;
2798 }
2799
2800 /*
2801 * Find the SKIP or first END pattern after the last START pattern.
2802 */
2803 for (;;)
2804 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002805 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002806 if (spp->sp_type != SPTYPE_START)
2807 break;
2808 ++idx;
2809 }
2810
2811 /*
2812 * Lookup the SKIP pattern (if present)
2813 */
2814 if (spp->sp_type == SPTYPE_SKIP)
2815 {
2816 spp_skip = spp;
2817 ++idx;
2818 }
2819 else
2820 spp_skip = NULL;
2821
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002822 // Setup external matches for syn_regexec().
Bram Moolenaar071d4272004-06-13 20:20:40 +00002823 unref_extmatch(re_extmatch_in);
2824 re_extmatch_in = ref_extmatch(start_ext);
2825
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002826 matchcol = startpos->col; // start looking for a match at sstart
2827 start_idx = idx; // remember the first END pattern.
2828 best_regmatch.startpos[0].col = 0; // avoid compiler warning
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002829
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002830 // use syntax iskeyword option
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002831 save_chartab(buf_chartab);
2832
Bram Moolenaar071d4272004-06-13 20:20:40 +00002833 for (;;)
2834 {
2835 /*
2836 * Find end pattern that matches first after "matchcol".
2837 */
2838 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002839 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002840 {
2841 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002842 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002843
Bram Moolenaar860cae12010-06-05 23:22:07 +02002844 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002845 if (spp->sp_type != SPTYPE_END) // past last END pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00002846 break;
2847 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2848 if (lc_col < 0)
2849 lc_col = 0;
2850
2851 regmatch.rmm_ic = spp->sp_ic;
2852 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002853 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
2854 IF_SYN_TIME(&spp->sp_time));
2855 spp->sp_prog = regmatch.regprog;
2856 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002857 {
2858 if (best_idx == -1 || regmatch.startpos[0].col
2859 < best_regmatch.startpos[0].col)
2860 {
2861 best_idx = idx;
2862 best_regmatch.startpos[0] = regmatch.startpos[0];
2863 best_regmatch.endpos[0] = regmatch.endpos[0];
2864 }
2865 }
2866 }
2867
2868 /*
2869 * If all end patterns have been tried, and there is no match, the
2870 * item continues until end-of-line.
2871 */
2872 if (best_idx == -1)
2873 break;
2874
2875 /*
2876 * If the skip pattern matches before the end pattern,
2877 * continue searching after the skip pattern.
2878 */
2879 if (spp_skip != NULL)
2880 {
2881 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002882 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002883
2884 if (lc_col < 0)
2885 lc_col = 0;
2886 regmatch.rmm_ic = spp_skip->sp_ic;
2887 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002888 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
2889 IF_SYN_TIME(&spp_skip->sp_time));
2890 spp_skip->sp_prog = regmatch.regprog;
2891 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00002892 <= best_regmatch.startpos[0].col)
2893 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01002894 int line_len;
2895
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002896 // Add offset to skip pattern match
Bram Moolenaar071d4272004-06-13 20:20:40 +00002897 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2898
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002899 // If the skip pattern goes on to the next line, there is no
2900 // match with an end pattern in this line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002901 if (pos.lnum > startpos->lnum)
2902 break;
2903
2904 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01002905 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002906
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002907 // take care of an empty match or negative offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00002908 if (pos.col <= matchcol)
2909 ++matchcol;
2910 else if (pos.col <= regmatch.endpos[0].col)
2911 matchcol = pos.col;
2912 else
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002913 // Be careful not to jump over the NUL at the end-of-line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002914 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01002915 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002916 ++matchcol)
2917 ;
2918
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002919 // if the skip pattern includes end-of-line, break here
Bram Moolenaar04bff882016-01-05 20:46:16 +01002920 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002921 break;
2922
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002923 continue; // start with first end pattern again
Bram Moolenaar071d4272004-06-13 20:20:40 +00002924 }
2925 }
2926
2927 /*
2928 * Match from start pattern to end pattern.
2929 * Correct for match and highlight offset of end pattern.
2930 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002931 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002932 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002933 // can't end before the start
Bram Moolenaar071d4272004-06-13 20:20:40 +00002934 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
2935 m_endpos->col = startpos->col;
2936
2937 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002938 // can't end before the start
Bram Moolenaar071d4272004-06-13 20:20:40 +00002939 if (end_endpos->lnum == startpos->lnum
2940 && end_endpos->col < startpos->col)
2941 end_endpos->col = startpos->col;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002942 // can't end after the match
Bram Moolenaar071d4272004-06-13 20:20:40 +00002943 limit_pos(end_endpos, m_endpos);
2944
2945 /*
2946 * If the end group is highlighted differently, adjust the pointers.
2947 */
2948 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
2949 {
2950 *end_idx = best_idx;
2951 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
2952 {
2953 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
2954 hl_endpos->col = best_regmatch.endpos[0].col;
2955 }
2956 else
2957 {
2958 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
2959 hl_endpos->col = best_regmatch.startpos[0].col;
2960 }
2961 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
2962
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002963 // can't end before the start
Bram Moolenaar071d4272004-06-13 20:20:40 +00002964 if (hl_endpos->lnum == startpos->lnum
2965 && hl_endpos->col < startpos->col)
2966 hl_endpos->col = startpos->col;
2967 limit_pos(hl_endpos, m_endpos);
2968
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002969 // now the match ends where the highlighting ends, it is turned
2970 // into the matchgroup for the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00002971 *m_endpos = *hl_endpos;
2972 }
2973 else
2974 {
2975 *end_idx = 0;
2976 *hl_endpos = *end_endpos;
2977 }
2978
2979 *flagsp = spp->sp_flags;
2980
2981 had_match = TRUE;
2982 break;
2983 }
2984
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002985 // no match for an END pattern in this line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002986 if (!had_match)
2987 m_endpos->lnum = 0;
2988
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002989 restore_chartab(buf_chartab);
2990
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002991 // Remove external matches.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002992 unref_extmatch(re_extmatch_in);
2993 re_extmatch_in = NULL;
2994}
2995
2996/*
2997 * Limit "pos" not to be after "limit".
2998 */
2999 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003000limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003001{
3002 if (pos->lnum > limit->lnum)
3003 *pos = *limit;
3004 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3005 pos->col = limit->col;
3006}
3007
3008/*
3009 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3010 */
3011 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003012limit_pos_zero(
3013 lpos_T *pos,
3014 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003015{
3016 if (pos->lnum == 0)
3017 *pos = *limit;
3018 else
3019 limit_pos(pos, limit);
3020}
3021
3022/*
3023 * Add offset to matched text for end of match or highlight.
3024 */
3025 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003026syn_add_end_off(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003027 lpos_T *result, // returned position
3028 regmmatch_T *regmatch, // start/end of match
3029 synpat_T *spp, // matched pattern
3030 int idx, // index of offset
3031 int extra) // extra chars for offset to start
Bram Moolenaar071d4272004-06-13 20:20:40 +00003032{
3033 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003034 int off;
3035 char_u *base;
3036 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003037
3038 if (spp->sp_off_flags & (1 << idx))
3039 {
3040 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003041 col = regmatch->startpos[0].col;
3042 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003043 }
3044 else
3045 {
3046 result->lnum = regmatch->endpos[0].lnum;
3047 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003048 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003049 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003050 // Don't go past the end of the line. Matters for "rs=e+2" when there
3051 // is a matchgroup. Watch out for match with last NL in the buffer.
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003052 if (result->lnum > syn_buf->b_ml.ml_line_count)
3053 col = 0;
3054 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003055 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003056 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3057 p = base + col;
3058 if (off > 0)
3059 {
3060 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003061 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003062 }
3063 else if (off < 0)
3064 {
3065 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003066 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003067 }
3068 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003069 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003070 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003071}
3072
3073/*
3074 * Add offset to matched text for start of match or highlight.
3075 * Avoid resulting column to become negative.
3076 */
3077 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003078syn_add_start_off(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003079 lpos_T *result, // returned position
3080 regmmatch_T *regmatch, // start/end of match
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003081 synpat_T *spp,
3082 int idx,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003083 int extra) // extra chars for offset to end
Bram Moolenaar071d4272004-06-13 20:20:40 +00003084{
3085 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003086 int off;
3087 char_u *base;
3088 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003089
3090 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3091 {
3092 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003093 col = regmatch->endpos[0].col;
3094 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003095 }
3096 else
3097 {
3098 result->lnum = regmatch->startpos[0].lnum;
3099 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003100 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003101 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003102 if (result->lnum > syn_buf->b_ml.ml_line_count)
3103 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003104 // a "\n" at the end of the pattern may take us below the last line
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003105 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003106 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003107 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003108 if (off != 0)
3109 {
3110 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3111 p = base + col;
3112 if (off > 0)
3113 {
3114 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003115 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003116 }
3117 else if (off < 0)
3118 {
3119 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003120 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003121 }
3122 col = (int)(p - base);
3123 }
3124 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003125}
3126
3127/*
3128 * Get current line in syntax buffer.
3129 */
3130 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003131syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003132{
3133 return ml_get_buf(syn_buf, current_lnum, FALSE);
3134}
3135
3136/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003137 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003138 * Returns TRUE when there is a match.
3139 */
3140 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003141syn_regexec(
3142 regmmatch_T *rmp,
3143 linenr_T lnum,
3144 colnr_T col,
3145 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003146{
Bram Moolenaar6f0cf622022-06-19 12:27:45 +01003147 int r;
3148 int timed_out = FALSE;
Bram Moolenaarf7512552013-06-06 14:55:19 +02003149#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003150 proftime_T pt;
3151
3152 if (syn_time_on)
3153 profile_start(&pt);
3154#endif
3155
Bram Moolenaarbcf94422018-06-23 14:21:42 +02003156 if (rmp->regprog == NULL)
3157 // This can happen if a previous call to vim_regexec_multi() tried to
3158 // use the NFA engine, which resulted in NFA_TOO_EXPENSIVE, and
3159 // compiling the pattern with the other engine fails.
3160 return FALSE;
3161
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003162 rmp->rmm_maxcol = syn_buf->b_p_smc;
Paul Ollis65745772022-06-05 16:55:54 +01003163 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, &timed_out);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003164
Bram Moolenaarf7512552013-06-06 14:55:19 +02003165#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003166 if (syn_time_on)
3167 {
3168 profile_end(&pt);
3169 profile_add(&st->total, &pt);
3170 if (profile_cmp(&pt, &st->slowest) < 0)
3171 st->slowest = pt;
3172 ++st->count;
3173 if (r > 0)
3174 ++st->match;
3175 }
3176#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003177#ifdef FEAT_RELTIME
Bram Moolenaar6f0cf622022-06-19 12:27:45 +01003178 if (timed_out && redrawtime_limit_set && !syn_win->w_s->b_syn_slow)
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003179 {
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003180 syn_win->w_s->b_syn_slow = TRUE;
Bram Moolenaar32526b32019-01-19 17:43:09 +01003181 msg(_("'redrawtime' exceeded, syntax highlighting disabled"));
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003182 }
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003183#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003184
3185 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003186 {
3187 rmp->startpos[0].lnum += lnum;
3188 rmp->endpos[0].lnum += lnum;
3189 return TRUE;
3190 }
3191 return FALSE;
3192}
3193
3194/*
3195 * Check one position in a line for a matching keyword.
3196 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003197 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003198 */
3199 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003200check_keyword_id(
3201 char_u *line,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003202 int startcol, // position in line to check for keyword
3203 int *endcolp, // return: character after found keyword
3204 long *flagsp, // return: flags of matching keyword
3205 short **next_listp, // return: next_list of matching keyword
3206 stateitem_T *cur_si, // item at the top of the stack
3207 int *ccharp UNUSED) // conceal substitution char
Bram Moolenaar071d4272004-06-13 20:20:40 +00003208{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003209 keyentry_T *kp;
3210 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003211 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003212 int kwlen;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003213 char_u keyword[MAXKEYWLEN + 1]; // assume max. keyword len is 80
Bram Moolenaardad6b692005-01-25 22:14:34 +00003214 hashtab_T *ht;
3215 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003216
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003217 // Find first character after the keyword. First character was already
3218 // checked.
Bram Moolenaardad6b692005-01-25 22:14:34 +00003219 kwp = line + startcol;
3220 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003221 do
3222 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003223 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003224 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003225 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00003226 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003227 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003228 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003229
Bram Moolenaardad6b692005-01-25 22:14:34 +00003230 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003231 return 0;
3232
3233 /*
3234 * Must make a copy of the keyword, so we can add a NUL and make it
3235 * lowercase.
3236 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003237 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003238
3239 /*
3240 * Try twice:
3241 * 1. matching case
3242 * 2. ignoring case
3243 */
3244 for (round = 1; round <= 2; ++round)
3245 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003246 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003247 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003248 continue;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003249 if (round == 2) // ignore case
Bram Moolenaardad6b692005-01-25 22:14:34 +00003250 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003251
3252 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003253 * Find keywords that match. There can be several with different
3254 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003255 * When current_next_list is non-zero accept only that group, otherwise:
3256 * Accept a not-contained keyword at toplevel.
3257 * Accept a keyword at other levels only if it is in the contains list.
3258 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003259 hi = hash_find(ht, keyword);
3260 if (!HASHITEM_EMPTY(hi))
3261 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003262 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003263 if (current_next_list != 0
3264 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3265 : (cur_si == NULL
3266 ? !(kp->flags & HL_CONTAINED)
3267 : in_id_list(cur_si, cur_si->si_cont_list,
3268 &kp->k_syn, kp->flags & HL_CONTAINED)))
3269 {
3270 *endcolp = startcol + kwlen;
3271 *flagsp = kp->flags;
3272 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003273#ifdef FEAT_CONCEAL
3274 *ccharp = kp->k_char;
3275#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003276 return kp->k_syn.id;
3277 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003278 }
3279 }
3280 return 0;
3281}
3282
3283/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003284 * Handle ":syntax conceal" command.
3285 */
3286 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003287syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003288{
3289#ifdef FEAT_CONCEAL
3290 char_u *arg = eap->arg;
3291 char_u *next;
3292
3293 eap->nextcmd = find_nextcmd(arg);
3294 if (eap->skip)
3295 return;
3296
3297 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003298 if (*arg == NUL)
3299 {
3300 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003301 msg("syntax conceal on");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003302 else
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003303 msg("syntax conceal off");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003304 }
3305 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003306 curwin->w_s->b_syn_conceal = TRUE;
3307 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3308 curwin->w_s->b_syn_conceal = FALSE;
3309 else
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003310 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003311#endif
3312}
3313
3314/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003315 * Handle ":syntax case" command.
3316 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003317 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003318syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003319{
3320 char_u *arg = eap->arg;
3321 char_u *next;
3322
3323 eap->nextcmd = find_nextcmd(arg);
3324 if (eap->skip)
3325 return;
3326
3327 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003328 if (*arg == NUL)
3329 {
3330 if (curwin->w_s->b_syn_ic)
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003331 msg("syntax case ignore");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003332 else
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003333 msg("syntax case match");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003334 }
3335 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003336 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003337 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003338 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003339 else
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003340 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003341}
3342
3343/*
3344 * Handle ":syntax foldlevel" command.
3345 */
3346 static void
3347syn_cmd_foldlevel(exarg_T *eap, int syncing UNUSED)
3348{
3349 char_u *arg = eap->arg;
3350 char_u *arg_end;
3351
3352 eap->nextcmd = find_nextcmd(arg);
3353 if (eap->skip)
3354 return;
3355
3356 if (*arg == NUL)
3357 {
3358 switch (curwin->w_s->b_syn_foldlevel)
3359 {
Dominique Pellecd53eed2022-02-05 18:53:06 +00003360 case SYNFLD_START: msg("syntax foldlevel start"); break;
3361 case SYNFLD_MINIMUM: msg("syntax foldlevel minimum"); break;
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003362 default: break;
3363 }
3364 return;
3365 }
3366
3367 arg_end = skiptowhite(arg);
3368 if (STRNICMP(arg, "start", 5) == 0 && arg_end - arg == 5)
3369 curwin->w_s->b_syn_foldlevel = SYNFLD_START;
3370 else if (STRNICMP(arg, "minimum", 7) == 0 && arg_end - arg == 7)
3371 curwin->w_s->b_syn_foldlevel = SYNFLD_MINIMUM;
3372 else
3373 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003374 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003375 return;
3376 }
3377
3378 arg = skipwhite(arg_end);
3379 if (*arg != NUL)
3380 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003381 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003382 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003383}
3384
3385/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003386 * Handle ":syntax spell" command.
3387 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003388 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003389syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003390{
3391 char_u *arg = eap->arg;
3392 char_u *next;
3393
3394 eap->nextcmd = find_nextcmd(arg);
3395 if (eap->skip)
3396 return;
3397
3398 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003399 if (*arg == NUL)
3400 {
3401 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
Dominique Pellecd53eed2022-02-05 18:53:06 +00003402 msg("syntax spell toplevel");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003403 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
Dominique Pellecd53eed2022-02-05 18:53:06 +00003404 msg("syntax spell notoplevel");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003405 else
Dominique Pellecd53eed2022-02-05 18:53:06 +00003406 msg("syntax spell default");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003407 }
3408 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003409 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003410 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003411 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003412 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003413 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003414 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003415 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003416 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003417 return;
3418 }
3419
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003420 // assume spell checking changed, force a redraw
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003421 redraw_win_later(curwin, UPD_NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003422}
3423
3424/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003425 * Handle ":syntax iskeyword" command.
3426 */
3427 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003428syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003429{
3430 char_u *arg = eap->arg;
3431 char_u save_chartab[32];
3432 char_u *save_isk;
3433
3434 if (eap->skip)
3435 return;
3436
3437 arg = skipwhite(arg);
3438 if (*arg == NUL)
3439 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003440 msg_puts("\n");
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003441 if (curwin->w_s->b_syn_isk != empty_option)
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003442 {
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003443 msg_puts("syntax iskeyword ");
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003444 msg_outtrans(curwin->w_s->b_syn_isk);
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003445 }
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003446 else
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003447 msg_outtrans((char_u *)_("syntax iskeyword not set"));
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003448 }
3449 else
3450 {
3451 if (STRNICMP(arg, "clear", 5) == 0)
3452 {
3453 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3454 (size_t)32);
3455 clear_string_option(&curwin->w_s->b_syn_isk);
3456 }
3457 else
3458 {
3459 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3460 save_isk = curbuf->b_p_isk;
3461 curbuf->b_p_isk = vim_strsave(arg);
3462
3463 buf_init_chartab(curbuf, FALSE);
3464 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3465 (size_t)32);
3466 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3467 clear_string_option(&curwin->w_s->b_syn_isk);
3468 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3469 curbuf->b_p_isk = save_isk;
3470 }
3471 }
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003472 redraw_win_later(curwin, UPD_NOT_VALID);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003473}
3474
3475/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003476 * Clear all syntax info for one buffer.
3477 */
3478 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003479syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003480{
3481 int i;
3482
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003483 block->b_syn_error = FALSE; // clear previous error
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003484#ifdef FEAT_RELTIME
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003485 block->b_syn_slow = FALSE; // clear previous timeout
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003486#endif
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003487 block->b_syn_ic = FALSE; // Use case, by default
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003488 block->b_syn_foldlevel = SYNFLD_START;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003489 block->b_syn_spell = SYNSPL_DEFAULT; // default spell checking
Bram Moolenaar860cae12010-06-05 23:22:07 +02003490 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003491#ifdef FEAT_CONCEAL
3492 block->b_syn_conceal = FALSE;
3493#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003494
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003495 // free the keywords
Bram Moolenaar860cae12010-06-05 23:22:07 +02003496 clear_keywtab(&block->b_keywtab);
3497 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003498
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003499 // free the syntax patterns
Bram Moolenaar860cae12010-06-05 23:22:07 +02003500 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3501 syn_clear_pattern(block, i);
3502 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003503
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003504 // free the syntax clusters
Bram Moolenaar860cae12010-06-05 23:22:07 +02003505 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3506 syn_clear_cluster(block, i);
3507 ga_clear(&block->b_syn_clusters);
3508 block->b_spell_cluster_id = 0;
3509 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003510
Bram Moolenaar860cae12010-06-05 23:22:07 +02003511 block->b_syn_sync_flags = 0;
3512 block->b_syn_sync_minlines = 0;
3513 block->b_syn_sync_maxlines = 0;
3514 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003515
Bram Moolenaar473de612013-06-08 18:19:48 +02003516 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003517 block->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003518 VIM_CLEAR(block->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003519#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003520 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003521#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003522 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003523
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003524 // free the stored states
Bram Moolenaar860cae12010-06-05 23:22:07 +02003525 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003526 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003527
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003528 // Reset the counter for ":syn include"
Bram Moolenaar42431a72011-04-01 14:44:59 +02003529 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003530}
3531
3532/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003533 * Get rid of ownsyntax for window "wp".
3534 */
3535 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003536reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003537{
3538 if (wp->w_s != &wp->w_buffer->b_s)
3539 {
3540 syntax_clear(wp->w_s);
3541 vim_free(wp->w_s);
3542 wp->w_s = &wp->w_buffer->b_s;
3543 }
3544}
3545
3546/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003547 * Clear syncing info for one buffer.
3548 */
3549 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003550syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003551{
3552 int i;
3553
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003554 // free the syntax patterns
Bram Moolenaar860cae12010-06-05 23:22:07 +02003555 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3556 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3557 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003558
Bram Moolenaar860cae12010-06-05 23:22:07 +02003559 curwin->w_s->b_syn_sync_flags = 0;
3560 curwin->w_s->b_syn_sync_minlines = 0;
3561 curwin->w_s->b_syn_sync_maxlines = 0;
3562 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003563
Bram Moolenaar473de612013-06-08 18:19:48 +02003564 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003565 curwin->w_s->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003566 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003567 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003568
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003569 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003570}
3571
3572/*
3573 * Remove one pattern from the buffer's pattern list.
3574 */
3575 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003576syn_remove_pattern(
3577 synblock_T *block,
3578 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003579{
3580 synpat_T *spp;
3581
Bram Moolenaar860cae12010-06-05 23:22:07 +02003582 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003583#ifdef FEAT_FOLDING
3584 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003585 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003586#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003587 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003588 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003589 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3590 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003591}
3592
3593/*
3594 * Clear and free one syntax pattern. When clearing all, must be called from
3595 * last to first!
3596 */
3597 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003598syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003599{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003600 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003601 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003602 // Only free sp_cont_list and sp_next_list of first start pattern
Bram Moolenaar860cae12010-06-05 23:22:07 +02003603 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003604 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003605 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3606 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3607 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003608 }
3609}
3610
3611/*
3612 * Clear and free one syntax cluster.
3613 */
3614 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003615syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003616{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003617 vim_free(SYN_CLSTR(block)[i].scl_name);
3618 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3619 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003620}
3621
3622/*
3623 * Handle ":syntax clear" command.
3624 */
3625 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003626syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003627{
3628 char_u *arg = eap->arg;
3629 char_u *arg_end;
3630 int id;
3631
3632 eap->nextcmd = find_nextcmd(arg);
3633 if (eap->skip)
3634 return;
3635
3636 /*
3637 * We have to disable this within ":syn include @group filename",
3638 * because otherwise @group would get deleted.
3639 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3640 * clear".
3641 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003642 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003643 return;
3644
Bram Moolenaar1966c242020-04-20 22:42:32 +02003645 if (ends_excmd2(eap->cmd, arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003646 {
3647 /*
3648 * No argument: Clear all syntax items.
3649 */
3650 if (syncing)
3651 syntax_sync_clear();
3652 else
3653 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003654 syntax_clear(curwin->w_s);
3655 if (curwin->w_s == &curwin->w_buffer->b_s)
3656 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003657 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003658 }
3659 }
3660 else
3661 {
3662 /*
3663 * Clear the group IDs that are in the argument.
3664 */
Bram Moolenaar1966c242020-04-20 22:42:32 +02003665 while (!ends_excmd2(eap->cmd, arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003666 {
3667 arg_end = skiptowhite(arg);
3668 if (*arg == '@')
3669 {
3670 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3671 if (id == 0)
3672 {
Bram Moolenaara9fa8c52023-01-02 18:10:04 +00003673 semsg(_(e_no_such_syntax_cluster_str_1), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003674 break;
3675 }
3676 else
3677 {
3678 /*
3679 * We can't physically delete a cluster without changing
3680 * the IDs of other clusters, so we do the next best thing
3681 * and make it empty.
3682 */
3683 short scl_id = id - SYNID_CLUSTER;
3684
Bram Moolenaard23a8232018-02-10 18:45:26 +01003685 VIM_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003686 }
3687 }
3688 else
3689 {
3690 id = syn_namen2id(arg, (int)(arg_end - arg));
3691 if (id == 0)
3692 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +02003693 semsg(_(e_no_such_highlight_group_name_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003694 break;
3695 }
3696 else
3697 syn_clear_one(id, syncing);
3698 }
3699 arg = skipwhite(arg_end);
3700 }
3701 }
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003702 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003703 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003704}
3705
3706/*
3707 * Clear one syntax group for the current buffer.
3708 */
3709 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003710syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003711{
3712 synpat_T *spp;
3713 int idx;
3714
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003715 // Clear keywords only when not ":syn sync clear group-name"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003716 if (!syncing)
3717 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003718 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3719 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003720 }
3721
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003722 // clear the patterns for "id"
Bram Moolenaar860cae12010-06-05 23:22:07 +02003723 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003724 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003725 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003726 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3727 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003728 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003729 }
3730}
3731
3732/*
3733 * Handle ":syntax on" command.
3734 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003735 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003736syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003737{
3738 syn_cmd_onoff(eap, "syntax");
3739}
3740
3741/*
3742 * Handle ":syntax enable" command.
3743 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003744 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003745syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003746{
Bram Moolenaarc8a9fe52021-11-20 19:50:59 +00003747 set_internal_string_var((char_u *)"g:syntax_cmd", (char_u *)"enable");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003748 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003749 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003750}
3751
3752/*
3753 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003754 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003755 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003756 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003757syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003758{
Bram Moolenaar63b91732021-08-05 20:40:03 +02003759 set_nextcmd(eap, eap->arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003760 if (!eap->skip)
3761 {
Bram Moolenaarc8a9fe52021-11-20 19:50:59 +00003762 set_internal_string_var((char_u *)"g:syntax_cmd", (char_u *)"reset");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003763 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003764 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003765 }
3766}
3767
3768/*
3769 * Handle ":syntax manual" command.
3770 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003771 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003772syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003773{
3774 syn_cmd_onoff(eap, "manual");
3775}
3776
3777/*
3778 * Handle ":syntax off" command.
3779 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003780 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003781syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003782{
3783 syn_cmd_onoff(eap, "nosyntax");
3784}
3785
3786 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003787syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003788{
3789 char_u buf[100];
3790
Bram Moolenaar63b91732021-08-05 20:40:03 +02003791 set_nextcmd(eap, eap->arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003792 if (!eap->skip)
3793 {
3794 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003795 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003796 do_cmdline_cmd(buf);
3797 }
3798}
3799
3800/*
3801 * Handle ":syntax [list]" command: list current syntax words.
3802 */
3803 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003804syn_cmd_list(
3805 exarg_T *eap,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003806 int syncing) // when TRUE: list syncing items
Bram Moolenaar071d4272004-06-13 20:20:40 +00003807{
3808 char_u *arg = eap->arg;
3809 int id;
3810 char_u *arg_end;
3811
3812 eap->nextcmd = find_nextcmd(arg);
3813 if (eap->skip)
3814 return;
3815
Bram Moolenaar860cae12010-06-05 23:22:07 +02003816 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003817 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003818 msg(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003819 return;
3820 }
3821
3822 if (syncing)
3823 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003824 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003825 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003826 msg_puts(_("syncing on C-style comments"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003827 syn_lines_msg();
3828 syn_match_msg();
3829 return;
3830 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003831 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003832 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003833 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003834 msg_puts(_("no syncing"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003835 else
3836 {
Bram Moolenaar99502802020-11-18 16:53:23 +01003837 if (curwin->w_s->b_syn_sync_minlines == MAXLNUM)
3838 msg_puts(_("syncing starts at the first line"));
3839 else
3840 {
3841 msg_puts(_("syncing starts "));
3842 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3843 msg_puts(_(" lines before top line"));
3844 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003845 syn_match_msg();
3846 }
3847 return;
3848 }
Bram Moolenaar32526b32019-01-19 17:43:09 +01003849 msg_puts_title(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003850 if (curwin->w_s->b_syn_sync_minlines > 0
3851 || curwin->w_s->b_syn_sync_maxlines > 0
3852 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003853 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003854 msg_puts(_("\nsyncing on items"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003855 syn_lines_msg();
3856 syn_match_msg();
3857 }
3858 }
3859 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01003860 msg_puts_title(_("\n--- Syntax items ---"));
Bram Moolenaar1966c242020-04-20 22:42:32 +02003861 if (ends_excmd2(eap->cmd, arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003862 {
3863 /*
3864 * No argument: List all group IDs and all syntax clusters.
3865 */
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02003866 for (id = 1; id <= highlight_num_groups() && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003867 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003868 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003869 syn_list_cluster(id);
3870 }
3871 else
3872 {
3873 /*
3874 * List the group IDs and syntax clusters that are in the argument.
3875 */
Bram Moolenaar1966c242020-04-20 22:42:32 +02003876 while (!ends_excmd2(eap->cmd, arg) && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003877 {
3878 arg_end = skiptowhite(arg);
3879 if (*arg == '@')
3880 {
3881 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3882 if (id == 0)
Bram Moolenaara9fa8c52023-01-02 18:10:04 +00003883 semsg(_(e_no_such_syntax_cluster_str_2), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003884 else
3885 syn_list_cluster(id - SYNID_CLUSTER);
3886 }
3887 else
3888 {
3889 id = syn_namen2id(arg, (int)(arg_end - arg));
3890 if (id == 0)
Bram Moolenaare29a27f2021-07-20 21:07:36 +02003891 semsg(_(e_no_such_highlight_group_name_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003892 else
3893 syn_list_one(id, syncing, TRUE);
3894 }
3895 arg = skipwhite(arg_end);
3896 }
3897 }
Bram Moolenaar63b91732021-08-05 20:40:03 +02003898 set_nextcmd(eap, arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003899}
3900
3901 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003902syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003903{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003904 if (curwin->w_s->b_syn_sync_maxlines > 0
3905 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003906 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003907 msg_puts("; ");
Bram Moolenaar99502802020-11-18 16:53:23 +01003908 if (curwin->w_s->b_syn_sync_minlines == MAXLNUM)
3909 msg_puts(_("from the first line"));
3910 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003911 {
Bram Moolenaar99502802020-11-18 16:53:23 +01003912 if (curwin->w_s->b_syn_sync_minlines > 0)
3913 {
3914 msg_puts(_("minimal "));
3915 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3916 if (curwin->w_s->b_syn_sync_maxlines)
3917 msg_puts(", ");
3918 }
3919 if (curwin->w_s->b_syn_sync_maxlines > 0)
3920 {
3921 msg_puts(_("maximal "));
3922 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
3923 }
3924 msg_puts(_(" lines before top line"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003925 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003926 }
3927}
3928
3929 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003930syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003931{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003932 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003933 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003934 msg_puts(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003935 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar32526b32019-01-19 17:43:09 +01003936 msg_puts(_(" line breaks"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003937 }
3938}
3939
3940static int last_matchgroup;
3941
3942struct name_list
3943{
3944 int flag;
3945 char *name;
3946};
3947
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01003948static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003949
3950/*
3951 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3952 */
3953 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003954syn_list_one(
3955 int id,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003956 int syncing, // when TRUE: list syncing items
3957 int link_only) // when TRUE; list link-only too
Bram Moolenaar071d4272004-06-13 20:20:40 +00003958{
3959 int attr;
3960 int idx;
3961 int did_header = FALSE;
3962 synpat_T *spp;
3963 static struct name_list namelist1[] =
3964 {
3965 {HL_DISPLAY, "display"},
3966 {HL_CONTAINED, "contained"},
3967 {HL_ONELINE, "oneline"},
3968 {HL_KEEPEND, "keepend"},
3969 {HL_EXTEND, "extend"},
3970 {HL_EXCLUDENL, "excludenl"},
3971 {HL_TRANSP, "transparent"},
3972 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003973#ifdef FEAT_CONCEAL
3974 {HL_CONCEAL, "conceal"},
3975 {HL_CONCEALENDS, "concealends"},
3976#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003977 {0, NULL}
3978 };
3979 static struct name_list namelist2[] =
3980 {
3981 {HL_SKIPWHITE, "skipwhite"},
3982 {HL_SKIPNL, "skipnl"},
3983 {HL_SKIPEMPTY, "skipempty"},
3984 {0, NULL}
3985 };
3986
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003987 attr = HL_ATTR(HLF_D); // highlight like directories
Bram Moolenaar071d4272004-06-13 20:20:40 +00003988
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003989 // list the keywords for "id"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003990 if (!syncing)
3991 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003992 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3993 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003994 did_header, attr);
3995 }
3996
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003997 // list the patterns for "id"
Bram Moolenaar860cae12010-06-05 23:22:07 +02003998 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003999 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004000 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004001 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4002 continue;
4003
4004 (void)syn_list_header(did_header, 999, id);
4005 did_header = TRUE;
4006 last_matchgroup = 0;
4007 if (spp->sp_type == SPTYPE_MATCH)
4008 {
4009 put_pattern("match", ' ', spp, attr);
4010 msg_putchar(' ');
4011 }
4012 else if (spp->sp_type == SPTYPE_START)
4013 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004014 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4015 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4016 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4017 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4018 while (idx < curwin->w_s->b_syn_patterns.ga_len
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004019 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004020 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004021 --idx;
4022 msg_putchar(' ');
4023 }
4024 syn_list_flags(namelist1, spp->sp_flags, attr);
4025
4026 if (spp->sp_cont_list != NULL)
4027 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4028
4029 if (spp->sp_syn.cont_in_list != NULL)
4030 put_id_list((char_u *)"containedin",
4031 spp->sp_syn.cont_in_list, attr);
4032
4033 if (spp->sp_next_list != NULL)
4034 {
4035 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4036 syn_list_flags(namelist2, spp->sp_flags, attr);
4037 }
4038 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4039 {
4040 if (spp->sp_flags & HL_SYNC_HERE)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004041 msg_puts_attr("grouphere", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004042 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004043 msg_puts_attr("groupthere", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004044 msg_putchar(' ');
4045 if (spp->sp_sync_idx >= 0)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004046 msg_outtrans(highlight_group_name(SYN_ITEMS(curwin->w_s)
4047 [spp->sp_sync_idx].sp_syn.id - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004048 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004049 msg_puts("NONE");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004050 msg_putchar(' ');
4051 }
4052 }
4053
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004054 // list the link, if there is one
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004055 if (highlight_link_id(id - 1) && (did_header || link_only) && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004056 {
4057 (void)syn_list_header(did_header, 999, id);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004058 msg_puts_attr("links to", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004059 msg_putchar(' ');
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004060 msg_outtrans(highlight_group_name(highlight_link_id(id - 1) - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004061 }
4062}
4063
4064 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004065syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004066{
4067 int i;
4068
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004069 for (i = 0; nlist[i].flag != 0; ++i)
4070 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004071 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004072 msg_puts_attr(nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004073 msg_putchar(' ');
4074 }
4075}
4076
4077/*
4078 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4079 */
4080 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004081syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004082{
4083 int endcol = 15;
4084
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004085 // slight hack: roughly duplicate the guts of syn_list_header()
Bram Moolenaar071d4272004-06-13 20:20:40 +00004086 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004087 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004088
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004089 if (msg_col >= endcol) // output at least one space
Bram Moolenaar071d4272004-06-13 20:20:40 +00004090 endcol = msg_col + 1;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004091 if (Columns <= endcol) // avoid hang for tiny window
Bram Moolenaar071d4272004-06-13 20:20:40 +00004092 endcol = Columns - 1;
4093
4094 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004095 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004096 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004097 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004098 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004099 }
4100 else
4101 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004102 msg_puts_attr("cluster", HL_ATTR(HLF_D));
4103 msg_puts("=NONE");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004104 }
4105}
4106
4107 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004108put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004109{
4110 short *p;
4111
Bram Moolenaar32526b32019-01-19 17:43:09 +01004112 msg_puts_attr((char *)name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004113 msg_putchar('=');
4114 for (p = list; *p; ++p)
4115 {
4116 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4117 {
4118 if (p[1])
Bram Moolenaar32526b32019-01-19 17:43:09 +01004119 msg_puts("ALLBUT");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004120 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004121 msg_puts("ALL");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004122 }
4123 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4124 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004125 msg_puts("TOP");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004126 }
4127 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4128 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004129 msg_puts("CONTAINED");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004130 }
4131 else if (*p >= SYNID_CLUSTER)
4132 {
4133 short scl_id = *p - SYNID_CLUSTER;
4134
4135 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004136 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004137 }
4138 else
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004139 msg_outtrans(highlight_group_name(*p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004140 if (p[1])
4141 msg_putchar(',');
4142 }
4143 msg_putchar(' ');
4144}
4145
4146 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004147put_pattern(
4148 char *s,
4149 int c,
4150 synpat_T *spp,
4151 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004152{
4153 long n;
4154 int mask;
4155 int first;
4156 static char *sepchars = "/+=-#@\"|'^&";
4157 int i;
4158
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004159 // May have to write "matchgroup=group"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004160 if (last_matchgroup != spp->sp_syn_match_id)
4161 {
4162 last_matchgroup = spp->sp_syn_match_id;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004163 msg_puts_attr("matchgroup", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004164 msg_putchar('=');
4165 if (last_matchgroup == 0)
4166 msg_outtrans((char_u *)"NONE");
4167 else
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004168 msg_outtrans(highlight_group_name(last_matchgroup - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004169 msg_putchar(' ');
4170 }
4171
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004172 // output the name of the pattern and an '=' or ' '
Bram Moolenaar32526b32019-01-19 17:43:09 +01004173 msg_puts_attr(s, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004174 msg_putchar(c);
4175
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004176 // output the pattern, in between a char that is not in the pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00004177 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4178 if (sepchars[++i] == NUL)
4179 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004180 i = 0; // no good char found, just use the first one
Bram Moolenaar071d4272004-06-13 20:20:40 +00004181 break;
4182 }
4183 msg_putchar(sepchars[i]);
4184 msg_outtrans(spp->sp_pattern);
4185 msg_putchar(sepchars[i]);
4186
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004187 // output any pattern options
Bram Moolenaar071d4272004-06-13 20:20:40 +00004188 first = TRUE;
4189 for (i = 0; i < SPO_COUNT; ++i)
4190 {
4191 mask = (1 << i);
4192 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4193 {
4194 if (!first)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004195 msg_putchar(','); // separate with commas
Bram Moolenaar32526b32019-01-19 17:43:09 +01004196 msg_puts(spo_name_tab[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004197 n = spp->sp_offsets[i];
4198 if (i != SPO_LC_OFF)
4199 {
4200 if (spp->sp_off_flags & mask)
4201 msg_putchar('s');
4202 else
4203 msg_putchar('e');
4204 if (n > 0)
4205 msg_putchar('+');
4206 }
4207 if (n || i == SPO_LC_OFF)
4208 msg_outnum(n);
4209 first = FALSE;
4210 }
4211 }
4212 msg_putchar(' ');
4213}
4214
4215/*
4216 * List or clear the keywords for one syntax group.
4217 * Return TRUE if the header has been printed.
4218 */
4219 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004220syn_list_keywords(
4221 int id,
4222 hashtab_T *ht,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004223 int did_header, // header has already been printed
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004224 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004225{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004226 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004227 hashitem_T *hi;
4228 keyentry_T *kp;
4229 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004230 int prev_contained = 0;
4231 short *prev_next_list = NULL;
4232 short *prev_cont_in_list = NULL;
4233 int prev_skipnl = 0;
4234 int prev_skipwhite = 0;
4235 int prev_skipempty = 0;
4236
Bram Moolenaar071d4272004-06-13 20:20:40 +00004237 /*
4238 * Unfortunately, this list of keywords is not sorted on alphabet but on
4239 * hash value...
4240 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004241 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004242 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004243 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004244 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004245 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004246 --todo;
4247 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004248 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004249 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004250 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004251 if (prev_contained != (kp->flags & HL_CONTAINED)
4252 || prev_skipnl != (kp->flags & HL_SKIPNL)
4253 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4254 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4255 || prev_cont_in_list != kp->k_syn.cont_in_list
4256 || prev_next_list != kp->next_list)
4257 outlen = 9999;
4258 else
4259 outlen = (int)STRLEN(kp->keyword);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004260 // output "contained" and "nextgroup" on each line
Bram Moolenaardad6b692005-01-25 22:14:34 +00004261 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004262 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004263 prev_contained = 0;
4264 prev_next_list = NULL;
4265 prev_cont_in_list = NULL;
4266 prev_skipnl = 0;
4267 prev_skipwhite = 0;
4268 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004269 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004270 did_header = TRUE;
4271 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004272 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004273 msg_puts_attr("contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004274 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004275 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004276 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004277 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004278 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004279 put_id_list((char_u *)"containedin",
4280 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004281 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004282 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004283 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004284 if (kp->next_list != prev_next_list)
4285 {
4286 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4287 msg_putchar(' ');
4288 prev_next_list = kp->next_list;
4289 if (kp->flags & HL_SKIPNL)
4290 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004291 msg_puts_attr("skipnl", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004292 msg_putchar(' ');
4293 prev_skipnl = (kp->flags & HL_SKIPNL);
4294 }
4295 if (kp->flags & HL_SKIPWHITE)
4296 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004297 msg_puts_attr("skipwhite", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004298 msg_putchar(' ');
4299 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4300 }
4301 if (kp->flags & HL_SKIPEMPTY)
4302 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004303 msg_puts_attr("skipempty", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004304 msg_putchar(' ');
4305 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4306 }
4307 }
4308 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004309 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004310 }
4311 }
4312 }
4313
4314 return did_header;
4315}
4316
4317 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004318syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004319{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004320 hashitem_T *hi;
4321 keyentry_T *kp;
4322 keyentry_T *kp_prev;
4323 keyentry_T *kp_next;
4324 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004325
Bram Moolenaardad6b692005-01-25 22:14:34 +00004326 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004327 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004328 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004329 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004330 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004331 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004332 --todo;
4333 kp_prev = NULL;
4334 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004335 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004336 if (kp->k_syn.id == id)
4337 {
4338 kp_next = kp->ke_next;
4339 if (kp_prev == NULL)
4340 {
4341 if (kp_next == NULL)
Bram Moolenaaref2c3252022-11-25 16:31:51 +00004342 hash_remove(ht, hi, "syntax clear keyword");
Bram Moolenaardad6b692005-01-25 22:14:34 +00004343 else
4344 hi->hi_key = KE2HIKEY(kp_next);
4345 }
4346 else
4347 kp_prev->ke_next = kp_next;
4348 vim_free(kp->next_list);
4349 vim_free(kp->k_syn.cont_in_list);
4350 vim_free(kp);
4351 kp = kp_next;
4352 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004354 {
4355 kp_prev = kp;
4356 kp = kp->ke_next;
4357 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004358 }
4359 }
4360 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004361 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004362}
4363
4364/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004365 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004366 */
4367 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004368clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004369{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004370 hashitem_T *hi;
4371 int todo;
4372 keyentry_T *kp;
4373 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004374
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004375 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004376 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004377 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004378 if (!HASHITEM_EMPTY(hi))
4379 {
4380 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004381 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004382 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004383 kp_next = kp->ke_next;
4384 vim_free(kp->next_list);
4385 vim_free(kp->k_syn.cont_in_list);
4386 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004387 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004388 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004389 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004390 hash_clear(ht);
4391 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004392}
4393
4394/*
4395 * Add a keyword to the list of keywords.
4396 */
4397 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004398add_keyword(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004399 char_u *name, // name of keyword
4400 int id, // group ID for this keyword
4401 int flags, // flags for this keyword
4402 short *cont_in_list, // containedin for this keyword
4403 short *next_list, // nextgroup for this keyword
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004404 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004405{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004406 keyentry_T *kp;
4407 hashtab_T *ht;
4408 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004409 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004410 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004411 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004412
Bram Moolenaar860cae12010-06-05 23:22:07 +02004413 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004414 name_ic = str_foldcase(name, (int)STRLEN(name),
4415 name_folded, MAXKEYWLEN + 1);
4416 else
4417 name_ic = name;
Bram Moolenaar47ed5532019-08-08 20:49:14 +02004418 kp = alloc(offsetof(keyentry_T, keyword) + STRLEN(name_ic) + 1);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004419 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004420 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004421 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004422 kp->k_syn.id = id;
4423 kp->k_syn.inc_tag = current_syn_inc_tag;
4424 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004425 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004426 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004427 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004428 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004429 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004430
Bram Moolenaar860cae12010-06-05 23:22:07 +02004431 if (curwin->w_s->b_syn_ic)
4432 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004433 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004434 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004435
Bram Moolenaardad6b692005-01-25 22:14:34 +00004436 hash = hash_hash(kp->keyword);
4437 hi = hash_lookup(ht, kp->keyword, hash);
4438 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004439 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004440 // new keyword, add to hashtable
Bram Moolenaardad6b692005-01-25 22:14:34 +00004441 kp->ke_next = NULL;
4442 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004443 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004444 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004445 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004446 // keyword already exists, prepend to list
Bram Moolenaardad6b692005-01-25 22:14:34 +00004447 kp->ke_next = HI2KE(hi);
4448 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004449 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004450}
4451
4452/*
4453 * Get the start and end of the group name argument.
4454 * Return a pointer to the first argument.
4455 * Return NULL if the end of the command was found instead of further args.
4456 */
4457 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004458get_group_name(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004459 char_u *arg, // start of the argument
4460 char_u **name_end) // pointer to end of the name
Bram Moolenaar071d4272004-06-13 20:20:40 +00004461{
4462 char_u *rest;
4463
4464 *name_end = skiptowhite(arg);
4465 rest = skipwhite(*name_end);
4466
4467 /*
4468 * Check if there are enough arguments. The first argument may be a
4469 * pattern, where '|' is allowed, so only check for NUL.
4470 */
4471 if (ends_excmd(*arg) || *rest == NUL)
4472 return NULL;
4473 return rest;
4474}
4475
4476/*
4477 * Check for syntax command option arguments.
4478 * This can be called at any place in the list of arguments, and just picks
4479 * out the arguments that are known. Can be called several times in a row to
4480 * collect all options in between other arguments.
4481 * Return a pointer to the next argument (which isn't an option).
4482 * Return NULL for any error;
4483 */
4484 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004485get_syn_options(
Bram Moolenaar1966c242020-04-20 22:42:32 +02004486 char_u *start, // next argument to be checked
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004487 syn_opt_arg_T *opt, // various things
Bram Moolenaarde318c52017-01-17 16:27:10 +01004488 int *conceal_char UNUSED,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004489 int skip) // TRUE if skipping over command
Bram Moolenaar071d4272004-06-13 20:20:40 +00004490{
Bram Moolenaar1966c242020-04-20 22:42:32 +02004491 char_u *arg = start;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004492 char_u *gname_start, *gname;
4493 int syn_id;
4494 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004495 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004496 int i;
4497 int fidx;
4498 static struct flag
4499 {
4500 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004501 int argtype;
4502 int flags;
4503 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4504 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4505 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4506 {"eExXtTeEnNdD", 0, HL_EXTEND},
4507 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4508 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4509 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4510 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4511 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4512 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4513 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4514 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4515 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004516 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4517 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4518 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004519 {"cCoOnNtTaAiInNsS", 1, 0},
4520 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4521 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004522 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004523 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004524
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004525 if (arg == NULL) // already detected error
Bram Moolenaar071d4272004-06-13 20:20:40 +00004526 return NULL;
4527
Bram Moolenaar860cae12010-06-05 23:22:07 +02004528#ifdef FEAT_CONCEAL
4529 if (curwin->w_s->b_syn_conceal)
4530 opt->flags |= HL_CONCEAL;
4531#endif
4532
Bram Moolenaar071d4272004-06-13 20:20:40 +00004533 for (;;)
4534 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004535 /*
4536 * This is used very often when a large number of keywords is defined.
4537 * Need to skip quickly when no option name is found.
4538 * Also avoid tolower(), it's slow.
4539 */
4540 if (strchr(first_letters, *arg) == NULL)
4541 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004542
K.Takataeeec2542021-06-02 13:28:16 +02004543 for (fidx = ARRAY_LENGTH(flagtab); --fidx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004544 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004545 p = flagtab[fidx].name;
4546 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4547 if (arg[len] != p[i] && arg[len] != p[i + 1])
4548 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004549 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004550 || (flagtab[fidx].argtype > 0
4551 ? arg[len] == '='
Bram Moolenaar1966c242020-04-20 22:42:32 +02004552 : ends_excmd2(start, arg + len))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004553 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004554 if (opt->keyword
4555 && (flagtab[fidx].flags == HL_DISPLAY
4556 || flagtab[fidx].flags == HL_FOLD
4557 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004558 // treat "display", "fold" and "extend" as a keyword
Bram Moolenaar071d4272004-06-13 20:20:40 +00004559 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004560 break;
4561 }
4562 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004563 if (fidx < 0) // no match found
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004564 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004565
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004566 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004567 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004568 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004569 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004570 emsg(_(e_contains_argument_not_accepted_here));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004571 return NULL;
4572 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004573 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004574 return NULL;
4575 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004576 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004577 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004578 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004579 return NULL;
4580 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004581 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004582 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004583 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004584 return NULL;
4585 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004586 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4587 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004588 // cchar=?
Bram Moolenaar860cae12010-06-05 23:22:07 +02004589 if (has_mbyte)
4590 {
Bram Moolenaar264b74f2019-01-24 17:18:42 +01004591#ifdef FEAT_CONCEAL
Bram Moolenaar860cae12010-06-05 23:22:07 +02004592 *conceal_char = mb_ptr2char(arg + 6);
Bram Moolenaar264b74f2019-01-24 17:18:42 +01004593#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004594 arg += mb_ptr2len(arg + 6) - 1;
4595 }
4596 else
Bram Moolenaar56be9502010-06-06 14:20:26 +02004597 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004598#ifdef FEAT_CONCEAL
4599 *conceal_char = arg[6];
4600#else
4601 ;
4602#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004603 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004604#ifdef FEAT_CONCEAL
4605 if (!vim_isprintc_strict(*conceal_char))
4606 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00004607 emsg(_(e_invalid_cchar_value));
Bram Moolenaar4124e722011-01-22 00:58:20 +01004608 return NULL;
4609 }
4610#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004611 arg = skipwhite(arg + 7);
4612 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004613 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004614 {
4615 opt->flags |= flagtab[fidx].flags;
4616 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004617
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004618 if (flagtab[fidx].flags == HL_SYNC_HERE
4619 || flagtab[fidx].flags == HL_SYNC_THERE)
4620 {
4621 if (opt->sync_idx == NULL)
4622 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004623 emsg(_(e_groupthere_not_accepted_here));
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004624 return NULL;
4625 }
4626 gname_start = arg;
4627 arg = skiptowhite(arg);
4628 if (gname_start == arg)
4629 return NULL;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02004630 gname = vim_strnsave(gname_start, arg - gname_start);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004631 if (gname == NULL)
4632 return NULL;
4633 if (STRCMP(gname, "NONE") == 0)
4634 *opt->sync_idx = NONE_IDX;
4635 else
4636 {
4637 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004638 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4639 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
Bram Moolenaar71ccd032020-06-12 22:59:11 +02004640 && SYN_ITEMS(curwin->w_s)[i].sp_type
4641 == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004642 {
4643 *opt->sync_idx = i;
4644 break;
4645 }
4646 if (i < 0)
4647 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004648 semsg(_(e_didnt_find_region_item_for_str), gname);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004649 vim_free(gname);
4650 return NULL;
4651 }
4652 }
4653
4654 vim_free(gname);
4655 arg = skipwhite(arg);
4656 }
4657#ifdef FEAT_FOLDING
4658 else if (flagtab[fidx].flags == HL_FOLD
4659 && foldmethodIsSyntax(curwin))
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004660 // Need to update folds later.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004661 foldUpdateAll(curwin);
4662#endif
4663 }
4664 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004665
4666 return arg;
4667}
4668
4669/*
4670 * Adjustments to syntax item when declared in a ":syn include"'d file.
4671 * Set the contained flag, and if the item is not already contained, add it
4672 * to the specified top-level group, if any.
4673 */
4674 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004675syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004676{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004677 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004678 return;
4679 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004680 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004681 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004682 // We have to alloc this, because syn_combine_list() will free it.
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004683 short *grp_list = ALLOC_MULT(short, 2);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004684 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004685
4686 if (grp_list != NULL)
4687 {
4688 grp_list[0] = id;
4689 grp_list[1] = 0;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004690 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list,
4691 &grp_list, CLUSTER_ADD);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004692 }
4693 }
4694}
4695
4696/*
4697 * Handle ":syntax include [@{group-name}] filename" command.
4698 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004699 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004700syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004701{
4702 char_u *arg = eap->arg;
4703 int sgl_id = 1;
4704 char_u *group_name_end;
4705 char_u *rest;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004706 char *errormsg = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004707 int prev_toplvl_grp;
4708 int prev_syn_inc_tag;
4709 int source = FALSE;
4710
4711 eap->nextcmd = find_nextcmd(arg);
4712 if (eap->skip)
4713 return;
4714
4715 if (arg[0] == '@')
4716 {
4717 ++arg;
4718 rest = get_group_name(arg, &group_name_end);
4719 if (rest == NULL)
4720 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004721 emsg(_(e_filename_required));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004722 return;
4723 }
4724 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004725 if (sgl_id == 0)
4726 return;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004727 // separate_nextcmd() and expand_filename() depend on this
Bram Moolenaar071d4272004-06-13 20:20:40 +00004728 eap->arg = rest;
4729 }
4730
4731 /*
4732 * Everything that's left, up to the next command, should be the
4733 * filename to include.
4734 */
Bram Moolenaar8071cb22019-07-12 17:58:01 +02004735 eap->argt |= (EX_XFILE | EX_NOSPC);
Bram Moolenaarac485062022-03-23 19:45:01 +00004736 separate_nextcmd(eap, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004737 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4738 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004739 // For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4740 // file. Need to expand the file name first. In other cases
4741 // ":runtime!" is used.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004742 source = TRUE;
4743 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4744 {
4745 if (errormsg != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004746 emsg(errormsg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004747 return;
4748 }
4749 }
4750
4751 /*
4752 * Save and restore the existing top-level grouplist id and ":syn
4753 * include" tag around the actual inclusion.
4754 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004755 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4756 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00004757 emsg(_(e_too_many_syntax_includes));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004758 return;
4759 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004760 prev_syn_inc_tag = current_syn_inc_tag;
4761 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004762 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4763 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01004764 if (source ? do_source(eap->arg, FALSE, DOSO_NONE, NULL) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004765 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004766 semsg(_(e_cant_open_file_str), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004767 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004768 current_syn_inc_tag = prev_syn_inc_tag;
4769}
4770
4771/*
4772 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4773 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004774 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004775syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004776{
4777 char_u *arg = eap->arg;
4778 char_u *group_name_end;
4779 int syn_id;
4780 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004781 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004782 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004783 char_u *kw;
4784 syn_opt_arg_T syn_opt_arg;
4785 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004786 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004787
4788 rest = get_group_name(arg, &group_name_end);
4789
4790 if (rest != NULL)
4791 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004792 if (eap->skip)
4793 syn_id = -1;
4794 else
4795 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004796 if (syn_id != 0)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004797 // allocate a buffer, for removing backslashes in the keyword
Bram Moolenaar964b3742019-05-24 18:54:09 +02004798 keyword_copy = alloc(STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004799 if (keyword_copy != NULL)
4800 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004801 syn_opt_arg.flags = 0;
4802 syn_opt_arg.keyword = TRUE;
4803 syn_opt_arg.sync_idx = NULL;
4804 syn_opt_arg.has_cont_list = FALSE;
4805 syn_opt_arg.cont_in_list = NULL;
4806 syn_opt_arg.next_list = NULL;
4807
Bram Moolenaar071d4272004-06-13 20:20:40 +00004808 /*
4809 * The options given apply to ALL keywords, so all options must be
4810 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004811 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004812 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004813 cnt = 0;
4814 p = keyword_copy;
Bram Moolenaar1966c242020-04-20 22:42:32 +02004815 for ( ; rest != NULL && !ends_excmd2(eap->arg, rest);
4816 rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004817 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004818 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4819 eap->skip);
Bram Moolenaar1966c242020-04-20 22:42:32 +02004820 if (rest == NULL || ends_excmd2(eap->arg, rest))
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004821 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004822 // Copy the keyword, removing backslashes, and add a NUL.
Bram Moolenaar1c465442017-03-12 20:10:05 +01004823 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004824 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004825 if (*rest == '\\' && rest[1] != NUL)
4826 ++rest;
4827 *p++ = *rest++;
4828 }
4829 *p++ = NUL;
4830 ++cnt;
4831 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004832
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004833 if (!eap->skip)
4834 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004835 // Adjust flags for use of ":syn include".
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004836 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4837
4838 /*
4839 * 2: Add an entry for each keyword.
4840 */
4841 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4842 {
4843 for (p = vim_strchr(kw, '['); ; )
4844 {
4845 if (p != NULL)
4846 *p = NUL;
4847 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004848 syn_opt_arg.cont_in_list,
4849 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004850 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004851 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004852 if (p[1] == NUL)
4853 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00004854 semsg(_(e_error_missing_rsb_str), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004855 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004856 }
4857 if (p[1] == ']')
4858 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004859 if (p[2] != NUL)
4860 {
Bram Moolenaard82a47d2022-01-05 20:24:39 +00004861 semsg(_(e_trailing_char_after_rsb_str_str),
4862 kw, &p[2]);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004863 goto error;
4864 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004865 kw = p + 1; // skip over the "]"
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004866 break;
4867 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004868 if (has_mbyte)
4869 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004870 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004871
4872 mch_memmove(p, p + 1, l);
4873 p += l;
4874 }
4875 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004876 {
4877 p[0] = p[1];
4878 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004879 }
4880 }
4881 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004882 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004883error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004884 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004885 vim_free(syn_opt_arg.cont_in_list);
4886 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004887 }
4888 }
4889
4890 if (rest != NULL)
Bram Moolenaar63b91732021-08-05 20:40:03 +02004891 set_nextcmd(eap, rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004892 else
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00004893 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004894
Bram Moolenaara4d158b2022-08-14 14:17:45 +01004895 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004896 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004897}
4898
4899/*
4900 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4901 *
4902 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4903 */
4904 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004905syn_cmd_match(
4906 exarg_T *eap,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004907 int syncing) // TRUE for ":syntax sync match .. "
Bram Moolenaar071d4272004-06-13 20:20:40 +00004908{
4909 char_u *arg = eap->arg;
4910 char_u *group_name_end;
4911 char_u *rest;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004912 synpat_T item; // the item found in the line
Bram Moolenaar071d4272004-06-13 20:20:40 +00004913 int syn_id;
4914 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004915 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004916 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004917 int conceal_char = NUL;
Bram Moolenaar1966c242020-04-20 22:42:32 +02004918 int orig_called_emsg = called_emsg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004919
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004920 // Isolate the group name, check for validity
Bram Moolenaar071d4272004-06-13 20:20:40 +00004921 rest = get_group_name(arg, &group_name_end);
4922
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004923 // Get options before the pattern
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004924 syn_opt_arg.flags = 0;
4925 syn_opt_arg.keyword = FALSE;
4926 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4927 syn_opt_arg.has_cont_list = TRUE;
4928 syn_opt_arg.cont_list = NULL;
4929 syn_opt_arg.cont_in_list = NULL;
4930 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01004931 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004932
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004933 // get the pattern.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004934 init_syn_patterns();
Bram Moolenaara80faa82020-04-12 19:37:17 +02004935 CLEAR_FIELD(item);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004936 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004937 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4938 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004939
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004940 // Get options after the pattern
Bram Moolenaarde318c52017-01-17 16:27:10 +01004941 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004942
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004943 if (rest != NULL) // all arguments are valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00004944 {
4945 /*
4946 * Check for trailing command and illegal trailing arguments.
4947 */
Bram Moolenaar63b91732021-08-05 20:40:03 +02004948 set_nextcmd(eap, rest);
Bram Moolenaar1966c242020-04-20 22:42:32 +02004949 if (!ends_excmd2(eap->cmd, rest) || eap->skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004950 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004951 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004952 && (syn_id = syn_check_group(arg,
4953 (int)(group_name_end - arg))) != 0)
4954 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004955 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004956 /*
4957 * Store the pattern in the syn_items list
4958 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004959 idx = curwin->w_s->b_syn_patterns.ga_len;
4960 SYN_ITEMS(curwin->w_s)[idx] = item;
4961 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4962 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4963 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4964 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4965 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4966 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4967 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4968 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004969 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004970#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02004971 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004972#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004973 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004974 curwin->w_s->b_syn_containedin = TRUE;
4975 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4976 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004977
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004978 // remember that we found a match for syncing on
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004979 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004980 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004981#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004982 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004983 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004984#endif
4985
Bram Moolenaara4d158b2022-08-14 14:17:45 +01004986 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004987 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
4988 return; // don't free the progs and patterns now
Bram Moolenaar071d4272004-06-13 20:20:40 +00004989 }
4990 }
4991
4992 /*
4993 * Something failed, free the allocated memory.
4994 */
Bram Moolenaar473de612013-06-08 18:19:48 +02004995 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004996 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004997 vim_free(syn_opt_arg.cont_list);
4998 vim_free(syn_opt_arg.cont_in_list);
4999 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005000
Bram Moolenaar1966c242020-04-20 22:42:32 +02005001 if (rest == NULL && called_emsg == orig_called_emsg)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005002 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005003}
5004
5005/*
5006 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5007 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5008 */
5009 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005010syn_cmd_region(
5011 exarg_T *eap,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005012 int syncing) // TRUE for ":syntax sync region .."
Bram Moolenaar071d4272004-06-13 20:20:40 +00005013{
5014 char_u *arg = eap->arg;
5015 char_u *group_name_end;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005016 char_u *rest; // next arg, NULL on error
Bram Moolenaar071d4272004-06-13 20:20:40 +00005017 char_u *key_end;
5018 char_u *key = NULL;
5019 char_u *p;
5020 int item;
5021#define ITEM_START 0
5022#define ITEM_SKIP 1
5023#define ITEM_END 2
5024#define ITEM_MATCHGROUP 3
5025 struct pat_ptr
5026 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005027 synpat_T *pp_synp; // pointer to syn_pattern
5028 int pp_matchgroup_id; // matchgroup ID
5029 struct pat_ptr *pp_next; // pointer to next pat_ptr
Bram Moolenaar071d4272004-06-13 20:20:40 +00005030 } *(pat_ptrs[3]);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005031 // patterns found in the line
Bram Moolenaar071d4272004-06-13 20:20:40 +00005032 struct pat_ptr *ppp;
5033 struct pat_ptr *ppp_next;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005034 int pat_count = 0; // nr of syn_patterns found
Bram Moolenaar071d4272004-06-13 20:20:40 +00005035 int syn_id;
5036 int matchgroup_id = 0;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005037 int not_enough = FALSE; // not enough arguments
5038 int illegal = FALSE; // illegal arguments
Bram Moolenaar071d4272004-06-13 20:20:40 +00005039 int success = FALSE;
5040 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005041 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005042 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005043
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005044 // Isolate the group name, check for validity
Bram Moolenaar071d4272004-06-13 20:20:40 +00005045 rest = get_group_name(arg, &group_name_end);
5046
5047 pat_ptrs[0] = NULL;
5048 pat_ptrs[1] = NULL;
5049 pat_ptrs[2] = NULL;
5050
5051 init_syn_patterns();
5052
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005053 syn_opt_arg.flags = 0;
5054 syn_opt_arg.keyword = FALSE;
5055 syn_opt_arg.sync_idx = NULL;
5056 syn_opt_arg.has_cont_list = TRUE;
5057 syn_opt_arg.cont_list = NULL;
5058 syn_opt_arg.cont_in_list = NULL;
5059 syn_opt_arg.next_list = NULL;
5060
Bram Moolenaar071d4272004-06-13 20:20:40 +00005061 /*
5062 * get the options, patterns and matchgroup.
5063 */
Bram Moolenaar1966c242020-04-20 22:42:32 +02005064 while (rest != NULL && !ends_excmd2(eap->cmd, rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005065 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005066 // Check for option arguments
Bram Moolenaarde318c52017-01-17 16:27:10 +01005067 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar1966c242020-04-20 22:42:32 +02005068 if (rest == NULL || ends_excmd2(eap->cmd, rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005069 break;
5070
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005071 // must be a pattern or matchgroup then
Bram Moolenaar071d4272004-06-13 20:20:40 +00005072 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005073 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005074 ++key_end;
5075 vim_free(key);
Bram Moolenaardf44a272020-06-07 20:49:05 +02005076 key = vim_strnsave_up(rest, key_end - rest);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005077 if (key == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00005078 {
5079 rest = NULL;
5080 break;
5081 }
5082 if (STRCMP(key, "MATCHGROUP") == 0)
5083 item = ITEM_MATCHGROUP;
5084 else if (STRCMP(key, "START") == 0)
5085 item = ITEM_START;
5086 else if (STRCMP(key, "END") == 0)
5087 item = ITEM_END;
5088 else if (STRCMP(key, "SKIP") == 0)
5089 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005090 if (pat_ptrs[ITEM_SKIP] != NULL) // one skip pattern allowed
Bram Moolenaar071d4272004-06-13 20:20:40 +00005091 {
5092 illegal = TRUE;
5093 break;
5094 }
5095 item = ITEM_SKIP;
5096 }
5097 else
5098 break;
5099 rest = skipwhite(key_end);
5100 if (*rest != '=')
5101 {
5102 rest = NULL;
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005103 semsg(_(e_missing_equal_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005104 break;
5105 }
5106 rest = skipwhite(rest + 1);
5107 if (*rest == NUL)
5108 {
5109 not_enough = TRUE;
5110 break;
5111 }
5112
5113 if (item == ITEM_MATCHGROUP)
5114 {
5115 p = skiptowhite(rest);
5116 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5117 matchgroup_id = 0;
5118 else
5119 {
5120 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5121 if (matchgroup_id == 0)
5122 {
5123 illegal = TRUE;
5124 break;
5125 }
5126 }
5127 rest = skipwhite(p);
5128 }
5129 else
5130 {
5131 /*
5132 * Allocate room for a syn_pattern, and link it in the list of
5133 * syn_patterns for this item, at the start (because the list is
5134 * used from end to start).
5135 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005136 ppp = ALLOC_ONE(struct pat_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005137 if (ppp == NULL)
5138 {
5139 rest = NULL;
5140 break;
5141 }
5142 ppp->pp_next = pat_ptrs[item];
5143 pat_ptrs[item] = ppp;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005144 ppp->pp_synp = ALLOC_CLEAR_ONE(synpat_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005145 if (ppp->pp_synp == NULL)
5146 {
5147 rest = NULL;
5148 break;
5149 }
5150
5151 /*
5152 * Get the syntax pattern and the following offset(s).
5153 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005154 // Enable the appropriate \z specials.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005155 if (item == ITEM_START)
5156 reg_do_extmatch = REX_SET;
5157 else if (item == ITEM_SKIP || item == ITEM_END)
5158 reg_do_extmatch = REX_USE;
5159 rest = get_syn_pattern(rest, ppp->pp_synp);
5160 reg_do_extmatch = 0;
5161 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005162 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005163 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5164 ppp->pp_matchgroup_id = matchgroup_id;
5165 ++pat_count;
5166 }
5167 }
5168 vim_free(key);
5169 if (illegal || not_enough)
5170 rest = NULL;
5171
5172 /*
5173 * Must have a "start" and "end" pattern.
5174 */
5175 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5176 pat_ptrs[ITEM_END] == NULL))
5177 {
5178 not_enough = TRUE;
5179 rest = NULL;
5180 }
5181
5182 if (rest != NULL)
5183 {
5184 /*
5185 * Check for trailing garbage or command.
5186 * If OK, add the item.
5187 */
Bram Moolenaar63b91732021-08-05 20:40:03 +02005188 set_nextcmd(eap, rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005189 if (!ends_excmd(*rest) || eap->skip)
5190 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005191 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005192 && (syn_id = syn_check_group(arg,
5193 (int)(group_name_end - arg))) != 0)
5194 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005195 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005196 /*
5197 * Store the start/skip/end in the syn_items list
5198 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005199 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005200 for (item = ITEM_START; item <= ITEM_END; ++item)
5201 {
5202 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5203 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005204 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5205 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5206 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005207 (item == ITEM_START) ? SPTYPE_START :
5208 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005209 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5210 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005211 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5212 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005213 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005214 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005215#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005216 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005217#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005218 if (item == ITEM_START)
5219 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005220 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005221 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005222 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005223 syn_opt_arg.cont_in_list;
5224 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005225 curwin->w_s->b_syn_containedin = TRUE;
5226 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005227 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005228 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005229 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005230 ++idx;
5231#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005232 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005233 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005234#endif
5235 }
5236 }
5237
Bram Moolenaara4d158b2022-08-14 14:17:45 +01005238 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005239 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
5240 success = TRUE; // don't free the progs and patterns now
Bram Moolenaar071d4272004-06-13 20:20:40 +00005241 }
5242 }
5243
5244 /*
5245 * Free the allocated memory.
5246 */
5247 for (item = ITEM_START; item <= ITEM_END; ++item)
5248 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5249 {
Bram Moolenaar4bbfb0f2019-08-31 15:28:02 +02005250 if (!success && ppp->pp_synp != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005251 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005252 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005253 vim_free(ppp->pp_synp->sp_pattern);
5254 }
5255 vim_free(ppp->pp_synp);
5256 ppp_next = ppp->pp_next;
5257 vim_free(ppp);
5258 }
5259
5260 if (!success)
5261 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005262 vim_free(syn_opt_arg.cont_list);
5263 vim_free(syn_opt_arg.cont_in_list);
5264 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005265 if (not_enough)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005266 semsg(_(e_not_enough_arguments_syntax_region_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005267 else if (illegal || rest == NULL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005268 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005269 }
5270}
5271
5272/*
5273 * A simple syntax group ID comparison function suitable for use in qsort()
5274 */
5275 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005276syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005277{
5278 const short *s1 = v1;
5279 const short *s2 = v2;
5280
5281 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5282}
5283
5284/*
5285 * Combines lists of syntax clusters.
5286 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5287 */
5288 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005289syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005290{
5291 int count1 = 0;
5292 int count2 = 0;
5293 short *g1;
5294 short *g2;
5295 short *clstr = NULL;
5296 int count;
5297 int round;
5298
5299 /*
5300 * Handle degenerate cases.
5301 */
5302 if (*clstr2 == NULL)
5303 return;
5304 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5305 {
5306 if (list_op == CLUSTER_REPLACE)
5307 vim_free(*clstr1);
5308 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5309 *clstr1 = *clstr2;
5310 else
5311 vim_free(*clstr2);
5312 return;
5313 }
5314
5315 for (g1 = *clstr1; *g1; g1++)
5316 ++count1;
5317 for (g2 = *clstr2; *g2; g2++)
5318 ++count2;
5319
5320 /*
5321 * For speed purposes, sort both lists.
5322 */
5323 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5324 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5325
5326 /*
5327 * We proceed in two passes; in round 1, we count the elements to place
5328 * in the new list, and in round 2, we allocate and populate the new
5329 * list. For speed, we use a mergesort-like method, adding the smaller
5330 * of the current elements in each list to the new list.
5331 */
5332 for (round = 1; round <= 2; round++)
5333 {
5334 g1 = *clstr1;
5335 g2 = *clstr2;
5336 count = 0;
5337
5338 /*
5339 * First, loop through the lists until one of them is empty.
5340 */
5341 while (*g1 && *g2)
5342 {
5343 /*
5344 * We always want to add from the first list.
5345 */
5346 if (*g1 < *g2)
5347 {
5348 if (round == 2)
5349 clstr[count] = *g1;
5350 count++;
5351 g1++;
5352 continue;
5353 }
5354 /*
5355 * We only want to add from the second list if we're adding the
5356 * lists.
5357 */
5358 if (list_op == CLUSTER_ADD)
5359 {
5360 if (round == 2)
5361 clstr[count] = *g2;
5362 count++;
5363 }
5364 if (*g1 == *g2)
5365 g1++;
5366 g2++;
5367 }
5368
5369 /*
5370 * Now add the leftovers from whichever list didn't get finished
5371 * first. As before, we only want to add from the second list if
5372 * we're adding the lists.
5373 */
5374 for (; *g1; g1++, count++)
5375 if (round == 2)
5376 clstr[count] = *g1;
5377 if (list_op == CLUSTER_ADD)
5378 for (; *g2; g2++, count++)
5379 if (round == 2)
5380 clstr[count] = *g2;
5381
5382 if (round == 1)
5383 {
5384 /*
5385 * If the group ended up empty, we don't need to allocate any
5386 * space for it.
5387 */
5388 if (count == 0)
5389 {
5390 clstr = NULL;
5391 break;
5392 }
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005393 clstr = ALLOC_MULT(short, count + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005394 if (clstr == NULL)
5395 break;
5396 clstr[count] = 0;
5397 }
5398 }
5399
5400 /*
5401 * Finally, put the new list in place.
5402 */
5403 vim_free(*clstr1);
5404 vim_free(*clstr2);
5405 *clstr1 = clstr;
5406}
5407
5408/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005409 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005410 * If it is not found, 0 is returned.
5411 */
5412 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005413syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005414{
5415 int i;
5416 char_u *name_u;
5417
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005418 // Avoid using stricmp() too much, it's slow on some systems
Bram Moolenaar071d4272004-06-13 20:20:40 +00005419 name_u = vim_strsave_up(name);
5420 if (name_u == NULL)
5421 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005422 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5423 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5424 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005425 break;
5426 vim_free(name_u);
5427 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5428}
5429
5430/*
5431 * Like syn_scl_name2id(), but take a pointer + length argument.
5432 */
5433 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005434syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005435{
5436 char_u *name;
5437 int id = 0;
5438
5439 name = vim_strnsave(linep, len);
5440 if (name != NULL)
5441 {
5442 id = syn_scl_name2id(name);
5443 vim_free(name);
5444 }
5445 return id;
5446}
5447
5448/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005449 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005450 * The argument is a pointer to the name and the length of the name.
5451 * If it doesn't exist yet, a new entry is created.
5452 * Return 0 for failure.
5453 */
5454 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005455syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005456{
5457 int id;
5458 char_u *name;
5459
5460 name = vim_strnsave(pp, len);
5461 if (name == NULL)
5462 return 0;
5463
5464 id = syn_scl_name2id(name);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005465 if (id == 0) // doesn't exist yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00005466 id = syn_add_cluster(name);
5467 else
5468 vim_free(name);
5469 return id;
5470}
5471
5472/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005473 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005474 * "name" must be an allocated string, it will be consumed.
5475 * Return 0 for failure.
5476 */
5477 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005478syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005479{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005480 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005481
5482 /*
5483 * First call for this growarray: init growing array.
5484 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005485 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005486 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005487 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5488 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005489 }
5490
Bram Moolenaar42431a72011-04-01 14:44:59 +02005491 len = curwin->w_s->b_syn_clusters.ga_len;
5492 if (len >= MAX_CLUSTER_ID)
5493 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00005494 emsg(_(e_too_many_syntax_clusters));
Bram Moolenaar42431a72011-04-01 14:44:59 +02005495 vim_free(name);
5496 return 0;
5497 }
5498
Bram Moolenaar071d4272004-06-13 20:20:40 +00005499 /*
5500 * Make room for at least one other cluster entry.
5501 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005502 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005503 {
5504 vim_free(name);
5505 return 0;
5506 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005507
Bram Moolenaara80faa82020-04-12 19:37:17 +02005508 CLEAR_POINTER(&(SYN_CLSTR(curwin->w_s)[len]));
Bram Moolenaar860cae12010-06-05 23:22:07 +02005509 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5510 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5511 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5512 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005513
Bram Moolenaar217ad922005-03-20 22:37:15 +00005514 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005515 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005516 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005517 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005518
Bram Moolenaar071d4272004-06-13 20:20:40 +00005519 return len + SYNID_CLUSTER;
5520}
5521
5522/*
5523 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5524 * [add={groupname},..] [remove={groupname},..]".
5525 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005526 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005527syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005528{
5529 char_u *arg = eap->arg;
5530 char_u *group_name_end;
5531 char_u *rest;
5532 int scl_id;
5533 short *clstr_list;
5534 int got_clstr = FALSE;
5535 int opt_len;
5536 int list_op;
5537
5538 eap->nextcmd = find_nextcmd(arg);
5539 if (eap->skip)
5540 return;
5541
5542 rest = get_group_name(arg, &group_name_end);
5543
5544 if (rest != NULL)
5545 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005546 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5547 if (scl_id == 0)
5548 return;
5549 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005550
5551 for (;;)
5552 {
5553 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005554 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005555 {
5556 opt_len = 3;
5557 list_op = CLUSTER_ADD;
5558 }
5559 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005560 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005561 {
5562 opt_len = 6;
5563 list_op = CLUSTER_SUBTRACT;
5564 }
5565 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005566 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005567 {
5568 opt_len = 8;
5569 list_op = CLUSTER_REPLACE;
5570 }
5571 else
5572 break;
5573
5574 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005575 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005576 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005577 semsg(_(e_invalid_argument_str), rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005578 break;
5579 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005580 if (scl_id >= 0)
5581 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005582 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005583 else
5584 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005585 got_clstr = TRUE;
5586 }
5587
5588 if (got_clstr)
5589 {
Bram Moolenaara4d158b2022-08-14 14:17:45 +01005590 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005591 syn_stack_free_all(curwin->w_s); // Need to recompute all.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005592 }
5593 }
5594
5595 if (!got_clstr)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005596 emsg(_(e_no_cluster_specified));
Bram Moolenaar1966c242020-04-20 22:42:32 +02005597 if (rest == NULL || !ends_excmd2(eap->cmd, rest))
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005598 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005599}
5600
5601/*
5602 * On first call for current buffer: Init growing array.
5603 */
5604 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005605init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005606{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005607 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5608 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005609}
5610
5611/*
5612 * Get one pattern for a ":syntax match" or ":syntax region" command.
5613 * Stores the pattern and program in a synpat_T.
5614 * Returns a pointer to the next argument, or NULL in case of an error.
5615 */
5616 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005617get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005618{
5619 char_u *end;
5620 int *p;
5621 int idx;
5622 char_u *cpo_save;
5623
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005624 // need at least three chars
Bram Moolenaar38219782015-08-11 15:27:13 +02005625 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005626 return NULL;
5627
Bram Moolenaare8c4abb2020-04-02 21:13:25 +02005628 end = skip_regexp(arg + 1, *arg, TRUE);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005629 if (*end != *arg) // end delimiter not found
Bram Moolenaar071d4272004-06-13 20:20:40 +00005630 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005631 semsg(_(e_pattern_delimiter_not_found_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005632 return NULL;
5633 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005634 // store the pattern and compiled regexp program
Bram Moolenaar71ccd032020-06-12 22:59:11 +02005635 if ((ci->sp_pattern = vim_strnsave(arg + 1, end - arg - 1)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005636 return NULL;
5637
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005638 // Make 'cpoptions' empty, to avoid the 'l' flag
Bram Moolenaar071d4272004-06-13 20:20:40 +00005639 cpo_save = p_cpo;
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01005640 p_cpo = empty_option;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005641 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5642 p_cpo = cpo_save;
5643
5644 if (ci->sp_prog == NULL)
5645 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005646 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005647#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005648 syn_clear_time(&ci->sp_time);
5649#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005650
5651 /*
5652 * Check for a match, highlight or region offset.
5653 */
5654 ++end;
5655 do
5656 {
5657 for (idx = SPO_COUNT; --idx >= 0; )
5658 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5659 break;
5660 if (idx >= 0)
5661 {
5662 p = &(ci->sp_offsets[idx]);
5663 if (idx != SPO_LC_OFF)
5664 switch (end[3])
5665 {
5666 case 's': break;
5667 case 'b': break;
5668 case 'e': idx += SPO_COUNT; break;
5669 default: idx = -1; break;
5670 }
5671 if (idx >= 0)
5672 {
5673 ci->sp_off_flags |= (1 << idx);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005674 if (idx == SPO_LC_OFF) // lc=99
Bram Moolenaar071d4272004-06-13 20:20:40 +00005675 {
5676 end += 3;
5677 *p = getdigits(&end);
5678
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005679 // "lc=" offset automatically sets "ms=" offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00005680 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5681 {
5682 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5683 ci->sp_offsets[SPO_MS_OFF] = *p;
5684 }
5685 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005686 else // yy=x+99
Bram Moolenaar071d4272004-06-13 20:20:40 +00005687 {
5688 end += 4;
5689 if (*end == '+')
5690 {
5691 ++end;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005692 *p = getdigits(&end); // positive offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00005693 }
5694 else if (*end == '-')
5695 {
5696 ++end;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005697 *p = -getdigits(&end); // negative offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00005698 }
5699 }
5700 if (*end != ',')
5701 break;
5702 ++end;
5703 }
5704 }
5705 } while (idx >= 0);
5706
Bram Moolenaar1966c242020-04-20 22:42:32 +02005707 if (!ends_excmd2(arg, end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005708 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005709 semsg(_(e_garbage_after_pattern_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005710 return NULL;
5711 }
5712 return skipwhite(end);
5713}
5714
5715/*
5716 * Handle ":syntax sync .." command.
5717 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005718 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005719syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005720{
5721 char_u *arg_start = eap->arg;
5722 char_u *arg_end;
5723 char_u *key = NULL;
5724 char_u *next_arg;
5725 int illegal = FALSE;
5726 int finished = FALSE;
5727 long n;
5728 char_u *cpo_save;
5729
Bram Moolenaar1966c242020-04-20 22:42:32 +02005730 if (ends_excmd2(eap->cmd, arg_start))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005731 {
5732 syn_cmd_list(eap, TRUE);
5733 return;
5734 }
5735
Bram Moolenaar1966c242020-04-20 22:42:32 +02005736 while (!ends_excmd2(eap->cmd, arg_start))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005737 {
5738 arg_end = skiptowhite(arg_start);
5739 next_arg = skipwhite(arg_end);
5740 vim_free(key);
Bram Moolenaardf44a272020-06-07 20:49:05 +02005741 key = vim_strnsave_up(arg_start, arg_end - arg_start);
Bram Moolenaar58bb61c2020-07-10 20:30:12 +02005742 if (key == NULL)
5743 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005744 if (STRCMP(key, "CCOMMENT") == 0)
5745 {
5746 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005747 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar1966c242020-04-20 22:42:32 +02005748 if (!ends_excmd2(eap->cmd, next_arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005749 {
5750 arg_end = skiptowhite(next_arg);
5751 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005752 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005753 (int)(arg_end - next_arg));
5754 next_arg = skipwhite(arg_end);
5755 }
5756 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005757 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005758 }
5759 else if ( STRNCMP(key, "LINES", 5) == 0
5760 || STRNCMP(key, "MINLINES", 8) == 0
5761 || STRNCMP(key, "MAXLINES", 8) == 0
5762 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5763 {
5764 if (key[4] == 'S')
5765 arg_end = key + 6;
5766 else if (key[0] == 'L')
5767 arg_end = key + 11;
5768 else
5769 arg_end = key + 9;
5770 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5771 {
5772 illegal = TRUE;
5773 break;
5774 }
5775 n = getdigits(&arg_end);
5776 if (!eap->skip)
5777 {
5778 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005779 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005780 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005781 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005782 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005783 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005784 }
5785 }
5786 else if (STRCMP(key, "FROMSTART") == 0)
5787 {
5788 if (!eap->skip)
5789 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005790 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5791 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005792 }
5793 }
5794 else if (STRCMP(key, "LINECONT") == 0)
5795 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005796 if (*next_arg == NUL) // missing pattern
Bram Moolenaar2795e212016-01-05 22:04:49 +01005797 {
5798 illegal = TRUE;
5799 break;
5800 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005801 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005802 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005803 emsg(_(e_syntax_sync_line_continuations_pattern_specified_twice));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005804 finished = TRUE;
5805 break;
5806 }
Bram Moolenaare8c4abb2020-04-02 21:13:25 +02005807 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005808 if (*arg_end != *next_arg) // end delimiter not found
Bram Moolenaar071d4272004-06-13 20:20:40 +00005809 {
5810 illegal = TRUE;
5811 break;
5812 }
5813
5814 if (!eap->skip)
5815 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005816 // store the pattern and compiled regexp program
Bram Moolenaar71ccd032020-06-12 22:59:11 +02005817 if ((curwin->w_s->b_syn_linecont_pat =
5818 vim_strnsave(next_arg + 1,
5819 arg_end - next_arg - 1)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005820 {
5821 finished = TRUE;
5822 break;
5823 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005824 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005825
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005826 // Make 'cpoptions' empty, to avoid the 'l' flag
Bram Moolenaar071d4272004-06-13 20:20:40 +00005827 cpo_save = p_cpo;
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01005828 p_cpo = empty_option;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005829 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005830 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005831 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005832#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005833 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5834#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005835
Bram Moolenaar860cae12010-06-05 23:22:07 +02005836 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005837 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01005838 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005839 finished = TRUE;
5840 break;
5841 }
5842 }
5843 next_arg = skipwhite(arg_end + 1);
5844 }
5845 else
5846 {
5847 eap->arg = next_arg;
5848 if (STRCMP(key, "MATCH") == 0)
5849 syn_cmd_match(eap, TRUE);
5850 else if (STRCMP(key, "REGION") == 0)
5851 syn_cmd_region(eap, TRUE);
5852 else if (STRCMP(key, "CLEAR") == 0)
5853 syn_cmd_clear(eap, TRUE);
5854 else
5855 illegal = TRUE;
5856 finished = TRUE;
5857 break;
5858 }
5859 arg_start = next_arg;
5860 }
5861 vim_free(key);
5862 if (illegal)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005863 semsg(_(e_illegal_arguments_str), arg_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005864 else if (!finished)
5865 {
Bram Moolenaar63b91732021-08-05 20:40:03 +02005866 set_nextcmd(eap, arg_start);
Bram Moolenaara4d158b2022-08-14 14:17:45 +01005867 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005868 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005869 }
5870}
5871
5872/*
5873 * Convert a line of highlight group names into a list of group ID numbers.
5874 * "arg" should point to the "contains" or "nextgroup" keyword.
5875 * "arg" is advanced to after the last group name.
5876 * Careful: the argument is modified (NULs added).
5877 * returns FAIL for some error, OK for success.
5878 */
5879 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005880get_id_list(
5881 char_u **arg,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005882 int keylen, // length of keyword
5883 short **list, // where to store the resulting list, if not
5884 // NULL, the list is silently skipped!
Bram Moolenaarde318c52017-01-17 16:27:10 +01005885 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005886{
5887 char_u *p = NULL;
5888 char_u *end;
5889 int round;
5890 int count;
5891 int total_count = 0;
5892 short *retval = NULL;
5893 char_u *name;
5894 regmatch_T regmatch;
5895 int id;
5896 int i;
5897 int failed = FALSE;
5898
5899 /*
5900 * We parse the list twice:
5901 * round == 1: count the number of items, allocate the array.
5902 * round == 2: fill the array with the items.
5903 * In round 1 new groups may be added, causing the number of items to
5904 * grow when a regexp is used. In that case round 1 is done once again.
5905 */
5906 for (round = 1; round <= 2; ++round)
5907 {
5908 /*
5909 * skip "contains"
5910 */
5911 p = skipwhite(*arg + keylen);
5912 if (*p != '=')
5913 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005914 semsg(_(e_missing_equal_sign_str), *arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005915 break;
5916 }
5917 p = skipwhite(p + 1);
Bram Moolenaar1966c242020-04-20 22:42:32 +02005918 if (ends_excmd2(*arg, p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005919 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005920 semsg(_(e_empty_argument_str), *arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005921 break;
5922 }
5923
5924 /*
5925 * parse the arguments after "contains"
5926 */
5927 count = 0;
Bram Moolenaar1966c242020-04-20 22:42:32 +02005928 while (!ends_excmd2(*arg, p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005929 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01005930 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005931 ;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005932 name = alloc(end - p + 3); // leave room for "^$"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005933 if (name == NULL)
5934 {
5935 failed = TRUE;
5936 break;
5937 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005938 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005939 if ( STRCMP(name + 1, "ALLBUT") == 0
5940 || STRCMP(name + 1, "ALL") == 0
5941 || STRCMP(name + 1, "TOP") == 0
5942 || STRCMP(name + 1, "CONTAINED") == 0)
5943 {
5944 if (TOUPPER_ASC(**arg) != 'C')
5945 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005946 semsg(_(e_str_not_allowed_here), name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005947 failed = TRUE;
5948 vim_free(name);
5949 break;
5950 }
5951 if (count != 0)
5952 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005953 semsg(_(e_str_must_be_first_in_contains_list), name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005954 failed = TRUE;
5955 vim_free(name);
5956 break;
5957 }
5958 if (name[1] == 'A')
Bram Moolenaar2e240bd2021-04-14 11:15:08 +02005959 id = SYNID_ALLBUT + current_syn_inc_tag;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005960 else if (name[1] == 'T')
Bram Moolenaar2e240bd2021-04-14 11:15:08 +02005961 {
5962 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
5963 id = curwin->w_s->b_syn_topgrp;
5964 else
5965 id = SYNID_TOP + current_syn_inc_tag;
5966 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005967 else
Bram Moolenaar2e240bd2021-04-14 11:15:08 +02005968 id = SYNID_CONTAINED + current_syn_inc_tag;
5969
Bram Moolenaar071d4272004-06-13 20:20:40 +00005970 }
5971 else if (name[1] == '@')
5972 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01005973 if (skip)
5974 id = -1;
5975 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01005976 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005977 }
5978 else
5979 {
5980 /*
5981 * Handle full group name.
5982 */
5983 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5984 id = syn_check_group(name + 1, (int)(end - p));
5985 else
5986 {
5987 /*
5988 * Handle match of regexp with group names.
5989 */
5990 *name = '^';
5991 STRCAT(name, "$");
5992 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5993 if (regmatch.regprog == NULL)
5994 {
5995 failed = TRUE;
5996 vim_free(name);
5997 break;
5998 }
5999
6000 regmatch.rm_ic = TRUE;
6001 id = 0;
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02006002 for (i = highlight_num_groups(); --i >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006003 {
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02006004 if (vim_regexec(&regmatch, highlight_group_name(i),
Bram Moolenaar071d4272004-06-13 20:20:40 +00006005 (colnr_T)0))
6006 {
6007 if (round == 2)
6008 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006009 // Got more items than expected; can happen
6010 // when adding items that match:
6011 // "contains=a.*b,axb".
6012 // Go back to first round
Bram Moolenaar071d4272004-06-13 20:20:40 +00006013 if (count >= total_count)
6014 {
6015 vim_free(retval);
6016 round = 1;
6017 }
6018 else
6019 retval[count] = i + 1;
6020 }
6021 ++count;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006022 id = -1; // remember that we found one
Bram Moolenaar071d4272004-06-13 20:20:40 +00006023 }
6024 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006025 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006026 }
6027 }
6028 vim_free(name);
6029 if (id == 0)
6030 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00006031 semsg(_(e_unknown_group_name_str), p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006032 failed = TRUE;
6033 break;
6034 }
6035 if (id > 0)
6036 {
6037 if (round == 2)
6038 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006039 // Got more items than expected, go back to first round
Bram Moolenaar071d4272004-06-13 20:20:40 +00006040 if (count >= total_count)
6041 {
6042 vim_free(retval);
6043 round = 1;
6044 }
6045 else
6046 retval[count] = id;
6047 }
6048 ++count;
6049 }
6050 p = skipwhite(end);
6051 if (*p != ',')
6052 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006053 p = skipwhite(p + 1); // skip comma in between arguments
Bram Moolenaar071d4272004-06-13 20:20:40 +00006054 }
6055 if (failed)
6056 break;
6057 if (round == 1)
6058 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006059 retval = ALLOC_MULT(short, count + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006060 if (retval == NULL)
6061 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006062 retval[count] = 0; // zero means end of the list
Bram Moolenaar071d4272004-06-13 20:20:40 +00006063 total_count = count;
6064 }
6065 }
6066
6067 *arg = p;
6068 if (failed || retval == NULL)
6069 {
6070 vim_free(retval);
6071 return FAIL;
6072 }
6073
6074 if (*list == NULL)
6075 *list = retval;
6076 else
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006077 vim_free(retval); // list already found, don't overwrite it
Bram Moolenaar071d4272004-06-13 20:20:40 +00006078
6079 return OK;
6080}
6081
6082/*
6083 * Make a copy of an ID list.
6084 */
6085 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006086copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006087{
6088 int len;
6089 int count;
6090 short *retval;
6091
6092 if (list == NULL)
6093 return NULL;
6094
6095 for (count = 0; list[count]; ++count)
6096 ;
6097 len = (count + 1) * sizeof(short);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006098 retval = alloc(len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006099 if (retval != NULL)
6100 mch_memmove(retval, list, (size_t)len);
6101
6102 return retval;
6103}
6104
6105/*
6106 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6107 * "cur_si" can be NULL if not checking the "containedin" list.
6108 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6109 * the current item.
6110 * This function is called very often, keep it fast!!
6111 */
6112 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006113in_id_list(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006114 stateitem_T *cur_si, // current item or NULL
6115 short *list, // id list
6116 struct sp_syn *ssp, // group id and ":syn include" tag of group
6117 int contained) // group id is contained
Bram Moolenaar071d4272004-06-13 20:20:40 +00006118{
6119 int retval;
6120 short *scl_list;
6121 short item;
6122 short id = ssp->id;
6123 static int depth = 0;
6124 int r;
6125
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006126 // If ssp has a "containedin" list and "cur_si" is in it, return TRUE.
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006127 if (cur_si != NULL && ssp->cont_in_list != NULL
6128 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006129 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006130 // Ignore transparent items without a contains argument. Double check
6131 // that we don't go back past the first one.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006132 while ((cur_si->si_flags & HL_TRANS_CONT)
6133 && cur_si > (stateitem_T *)(current_state.ga_data))
6134 --cur_si;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006135 // cur_si->si_idx is -1 for keywords, these never contain anything.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006136 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006137 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6138 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006139 return TRUE;
6140 }
6141
6142 if (list == NULL)
6143 return FALSE;
6144
6145 /*
6146 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6147 * inside anything. Only allow not-contained groups.
6148 */
6149 if (list == ID_LIST_ALL)
6150 return !contained;
6151
6152 /*
6153 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6154 * contains list. We also require that "id" is at the same ":syn include"
6155 * level as the list.
6156 */
6157 item = *list;
6158 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6159 {
6160 if (item < SYNID_TOP)
6161 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006162 // ALL or ALLBUT: accept all groups in the same file
Bram Moolenaar071d4272004-06-13 20:20:40 +00006163 if (item - SYNID_ALLBUT != ssp->inc_tag)
6164 return FALSE;
6165 }
6166 else if (item < SYNID_CONTAINED)
6167 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006168 // TOP: accept all not-contained groups in the same file
Bram Moolenaar071d4272004-06-13 20:20:40 +00006169 if (item - SYNID_TOP != ssp->inc_tag || contained)
6170 return FALSE;
6171 }
6172 else
6173 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006174 // CONTAINED: accept all contained groups in the same file
Bram Moolenaar071d4272004-06-13 20:20:40 +00006175 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6176 return FALSE;
6177 }
6178 item = *++list;
6179 retval = FALSE;
6180 }
6181 else
6182 retval = TRUE;
6183
6184 /*
6185 * Return "retval" if id is in the contains list.
6186 */
6187 while (item != 0)
6188 {
6189 if (item == id)
6190 return retval;
6191 if (item >= SYNID_CLUSTER)
6192 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006193 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006194 // restrict recursiveness to 30 to avoid an endless loop for a
6195 // cluster that includes itself (indirectly)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006196 if (scl_list != NULL && depth < 30)
6197 {
6198 ++depth;
6199 r = in_id_list(NULL, scl_list, ssp, contained);
6200 --depth;
6201 if (r)
6202 return retval;
6203 }
6204 }
6205 item = *++list;
6206 }
6207 return !retval;
6208}
6209
6210struct subcommand
6211{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006212 char *name; // subcommand name
6213 void (*func)(exarg_T *, int); // function to call
Bram Moolenaar071d4272004-06-13 20:20:40 +00006214};
6215
6216static struct subcommand subcommands[] =
6217{
6218 {"case", syn_cmd_case},
6219 {"clear", syn_cmd_clear},
6220 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006221 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006222 {"enable", syn_cmd_enable},
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006223 {"foldlevel", syn_cmd_foldlevel},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006224 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006225 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006226 {"keyword", syn_cmd_keyword},
6227 {"list", syn_cmd_list},
6228 {"manual", syn_cmd_manual},
6229 {"match", syn_cmd_match},
6230 {"on", syn_cmd_on},
6231 {"off", syn_cmd_off},
6232 {"region", syn_cmd_region},
6233 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006234 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006235 {"sync", syn_cmd_sync},
6236 {"", syn_cmd_list},
6237 {NULL, NULL}
6238};
6239
6240/*
6241 * ":syntax".
6242 * This searches the subcommands[] table for the subcommand name, and calls a
6243 * syntax_subcommand() function to do the rest.
6244 */
6245 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006246ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006247{
6248 char_u *arg = eap->arg;
6249 char_u *subcmd_end;
6250 char_u *subcmd_name;
6251 int i;
6252
6253 syn_cmdlinep = eap->cmdlinep;
6254
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006255 // isolate subcommand name
Bram Moolenaar071d4272004-06-13 20:20:40 +00006256 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6257 ;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02006258 subcmd_name = vim_strnsave(arg, subcmd_end - arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006259 if (subcmd_name != NULL)
6260 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006261 if (eap->skip) // skip error messages for all subcommands
Bram Moolenaar071d4272004-06-13 20:20:40 +00006262 ++emsg_skip;
6263 for (i = 0; ; ++i)
6264 {
6265 if (subcommands[i].name == NULL)
6266 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00006267 semsg(_(e_invalid_syntax_subcommand_str), subcmd_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006268 break;
6269 }
6270 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6271 {
6272 eap->arg = skipwhite(subcmd_end);
6273 (subcommands[i].func)(eap, FALSE);
6274 break;
6275 }
6276 }
6277 vim_free(subcmd_name);
6278 if (eap->skip)
6279 --emsg_skip;
6280 }
6281}
6282
Bram Moolenaar860cae12010-06-05 23:22:07 +02006283 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006284ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006285{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006286 char_u *old_value;
6287 char_u *new_value;
6288
Bram Moolenaar860cae12010-06-05 23:22:07 +02006289 if (curwin->w_s == &curwin->w_buffer->b_s)
6290 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006291 curwin->w_s = ALLOC_ONE(synblock_T);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006292 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006293 hash_init(&curwin->w_s->b_keywtab);
6294 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006295#ifdef FEAT_SPELL
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006296 // TODO: keep the spell checking as it was.
6297 curwin->w_p_spell = FALSE; // No spell checking
Bram Moolenaard1f76af2020-09-13 22:37:34 +02006298 // make sure option values are "empty_option" instead of NULL
Bram Moolenaar860cae12010-06-05 23:22:07 +02006299 clear_string_option(&curwin->w_s->b_p_spc);
6300 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006301 clear_string_option(&curwin->w_s->b_p_spl);
Bram Moolenaard1f76af2020-09-13 22:37:34 +02006302 clear_string_option(&curwin->w_s->b_p_spo);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006303#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006304 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006305 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006306
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006307 // save value of b:current_syntax
Bram Moolenaar1950c352010-06-06 15:21:10 +02006308 old_value = get_var_value((char_u *)"b:current_syntax");
6309 if (old_value != NULL)
6310 old_value = vim_strsave(old_value);
6311
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006312 // Apply the "syntax" autocommand event, this finds and loads the syntax
6313 // file.
Bram Moolenaar860cae12010-06-05 23:22:07 +02006314 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006315
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006316 // move value of b:current_syntax to w:current_syntax
Bram Moolenaar1950c352010-06-06 15:21:10 +02006317 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006318 if (new_value != NULL)
6319 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006320
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006321 // restore value of b:current_syntax
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006322 if (old_value == NULL)
6323 do_unlet((char_u *)"b:current_syntax", TRUE);
6324 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006325 {
6326 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6327 vim_free(old_value);
6328 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006329}
6330
6331 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006332syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006333{
6334 return (win->w_s->b_syn_patterns.ga_len != 0
6335 || win->w_s->b_syn_clusters.ga_len != 0
6336 || win->w_s->b_keywtab.ht_used > 0
6337 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006338}
6339
Bram Moolenaar071d4272004-06-13 20:20:40 +00006340
6341static enum
6342{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006343 EXP_SUBCMD, // expand ":syn" sub-commands
6344 EXP_CASE, // expand ":syn case" arguments
6345 EXP_SPELL, // expand ":syn spell" arguments
bfredlaf9a6002022-08-26 21:58:31 +01006346 EXP_SYNC, // expand ":syn sync" arguments
6347 EXP_CLUSTER // expand ":syn list @cluster" arguments
Bram Moolenaar071d4272004-06-13 20:20:40 +00006348} expand_what;
6349
Bram Moolenaar4f688582007-07-24 12:34:30 +00006350/*
6351 * Reset include_link, include_default, include_none to 0.
6352 * Called when we are done expanding.
6353 */
6354 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006355reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006356{
6357 include_link = include_default = include_none = 0;
6358}
6359
6360/*
6361 * Handle command line completion for :match and :echohl command: Add "None"
6362 * as highlight group.
6363 */
6364 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006365set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006366{
6367 xp->xp_context = EXPAND_HIGHLIGHT;
6368 xp->xp_pattern = arg;
6369 include_none = 1;
6370}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006371
6372/*
6373 * Handle command line completion for :syntax command.
6374 */
6375 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006376set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006377{
6378 char_u *p;
6379
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006380 // Default: expand subcommands
Bram Moolenaar071d4272004-06-13 20:20:40 +00006381 xp->xp_context = EXPAND_SYNTAX;
6382 expand_what = EXP_SUBCMD;
6383 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006384 include_link = 0;
6385 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006386
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006387 // (part of) subcommand already typed
Bram Moolenaar071d4272004-06-13 20:20:40 +00006388 if (*arg != NUL)
6389 {
6390 p = skiptowhite(arg);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006391 if (*p != NUL) // past first word
Bram Moolenaar071d4272004-06-13 20:20:40 +00006392 {
6393 xp->xp_pattern = skipwhite(p);
6394 if (*skiptowhite(xp->xp_pattern) != NUL)
6395 xp->xp_context = EXPAND_NOTHING;
6396 else if (STRNICMP(arg, "case", p - arg) == 0)
6397 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006398 else if (STRNICMP(arg, "spell", p - arg) == 0)
6399 expand_what = EXP_SPELL;
6400 else if (STRNICMP(arg, "sync", p - arg) == 0)
6401 expand_what = EXP_SYNC;
bfredlaf9a6002022-08-26 21:58:31 +01006402 else if (STRNICMP(arg, "list", p - arg) == 0)
6403 {
6404 p = skipwhite(p);
6405 if (*p == '@')
6406 expand_what = EXP_CLUSTER;
6407 else
6408 xp->xp_context = EXPAND_HIGHLIGHT;
6409 }
6410 else if (STRNICMP(arg, "keyword", p - arg) == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00006411 || STRNICMP(arg, "region", p - arg) == 0
bfredlaf9a6002022-08-26 21:58:31 +01006412 || STRNICMP(arg, "match", p - arg) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006413 xp->xp_context = EXPAND_HIGHLIGHT;
6414 else
6415 xp->xp_context = EXPAND_NOTHING;
6416 }
6417 }
6418}
6419
Bram Moolenaar071d4272004-06-13 20:20:40 +00006420/*
6421 * Function given to ExpandGeneric() to obtain the list syntax names for
6422 * expansion.
6423 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006424 char_u *
Bram Moolenaar5ff595d2022-08-26 22:36:41 +01006425get_syntax_name(expand_T *xp, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006426{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006427 switch (expand_what)
6428 {
6429 case EXP_SUBCMD:
6430 return (char_u *)subcommands[idx].name;
6431 case EXP_CASE:
6432 {
6433 static char *case_args[] = {"match", "ignore", NULL};
6434 return (char_u *)case_args[idx];
6435 }
6436 case EXP_SPELL:
6437 {
6438 static char *spell_args[] =
6439 {"toplevel", "notoplevel", "default", NULL};
6440 return (char_u *)spell_args[idx];
6441 }
6442 case EXP_SYNC:
6443 {
6444 static char *sync_args[] =
6445 {"ccomment", "clear", "fromstart",
6446 "linebreaks=", "linecont", "lines=", "match",
6447 "maxlines=", "minlines=", "region", NULL};
6448 return (char_u *)sync_args[idx];
6449 }
bfredlaf9a6002022-08-26 21:58:31 +01006450 case EXP_CLUSTER:
6451 {
6452 if (idx < curwin->w_s->b_syn_clusters.ga_len)
6453 {
Bram Moolenaar5ff595d2022-08-26 22:36:41 +01006454 vim_snprintf((char *)xp->xp_buf, EXPAND_BUF_LEN, "@%s",
bfredlaf9a6002022-08-26 21:58:31 +01006455 SYN_CLSTR(curwin->w_s)[idx].scl_name);
Bram Moolenaar5ff595d2022-08-26 22:36:41 +01006456 return xp->xp_buf;
bfredlaf9a6002022-08-26 21:58:31 +01006457 }
6458 else
6459 return NULL;
6460 }
Bram Moolenaar2d028392017-01-08 18:28:22 +01006461 }
6462 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006463}
6464
Bram Moolenaar071d4272004-06-13 20:20:40 +00006465
Bram Moolenaar071d4272004-06-13 20:20:40 +00006466/*
6467 * Function called for expression evaluation: get syntax ID at file position.
6468 */
6469 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006470syn_get_id(
6471 win_T *wp,
6472 long lnum,
6473 colnr_T col,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006474 int trans, // remove transparency
6475 int *spellp, // return: can do spell checking
6476 int keep_state) // keep state of char at "col"
Bram Moolenaar071d4272004-06-13 20:20:40 +00006477{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006478 // When the position is not after the current position and in the same
zeertzjqca7e86c2022-04-16 16:49:24 +01006479 // line of the same window with the same buffer, need to restart parsing.
6480 if (wp != syn_win
6481 || wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006482 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006483 || col < current_col)
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006484 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006485 else if (wp->w_buffer == syn_buf
6486 && lnum == current_lnum
6487 && col > current_col)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006488 // next_match may not be correct when moving around, e.g. with the
6489 // "skip" expression in searchpair()
Bram Moolenaar6773a342016-01-19 20:52:44 +01006490 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006491
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006492 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006493
6494 return (trans ? current_trans_id : current_id);
6495}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006496
Bram Moolenaar860cae12010-06-05 23:22:07 +02006497#if defined(FEAT_CONCEAL) || defined(PROTO)
6498/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006499 * Get extra information about the syntax item. Must be called right after
6500 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006501 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006502 * Returns the current flags.
6503 */
6504 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006505get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006506{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006507 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006508 return current_flags;
6509}
6510
6511/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006512 * Return conceal substitution character
6513 */
6514 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006515syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006516{
6517 return current_sub_char;
6518}
6519#endif
6520
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006521#if defined(FEAT_EVAL) || defined(PROTO)
6522/*
6523 * Return the syntax ID at position "i" in the current stack.
6524 * The caller must have called syn_get_id() before to fill the stack.
6525 * Returns -1 when "i" is out of range.
6526 */
6527 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006528syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006529{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006530 if (i >= current_state.ga_len)
6531 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006532 // Need to invalidate the state, because we didn't properly finish it
6533 // for the last character, "keep_state" was TRUE.
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006534 invalidate_current_state();
6535 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006536 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006537 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006538 return CUR_STATE(i).si_id;
6539}
6540#endif
6541
Bram Moolenaar071d4272004-06-13 20:20:40 +00006542#if defined(FEAT_FOLDING) || defined(PROTO)
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006543 static int
6544syn_cur_foldlevel(void)
6545{
6546 int level = 0;
6547 int i;
6548
6549 for (i = 0; i < current_state.ga_len; ++i)
6550 if (CUR_STATE(i).si_flags & HL_FOLD)
6551 ++level;
6552 return level;
6553}
6554
Bram Moolenaar071d4272004-06-13 20:20:40 +00006555/*
6556 * Function called to get folding level for line "lnum" in window "wp".
6557 */
6558 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006559syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006560{
6561 int level = 0;
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006562 int low_level;
6563 int cur_level;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006564
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006565 // Return quickly when there are no fold items at all.
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006566 if (wp->w_s->b_syn_folditems != 0
6567 && !wp->w_s->b_syn_error
6568# ifdef SYN_TIME_LIMIT
6569 && !wp->w_s->b_syn_slow
6570# endif
6571 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006572 {
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006573 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006574
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006575 // Start with the fold level at the start of the line.
6576 level = syn_cur_foldlevel();
6577
6578 if (wp->w_s->b_syn_foldlevel == SYNFLD_MINIMUM)
6579 {
6580 // Find the lowest fold level that is followed by a higher one.
6581 cur_level = level;
6582 low_level = cur_level;
6583 while (!current_finished)
6584 {
6585 (void)syn_current_attr(FALSE, FALSE, NULL, FALSE);
6586 cur_level = syn_cur_foldlevel();
6587 if (cur_level < low_level)
6588 low_level = cur_level;
6589 else if (cur_level > low_level)
6590 level = low_level;
6591 ++current_col;
6592 }
6593 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006594 }
6595 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006596 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006597 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006598 if (level < 0)
6599 level = 0;
6600 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006601 return level;
6602}
6603#endif
6604
Bram Moolenaar01615492015-02-03 13:00:38 +01006605#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006606/*
6607 * ":syntime".
6608 */
6609 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006610ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006611{
6612 if (STRCMP(eap->arg, "on") == 0)
6613 syn_time_on = TRUE;
6614 else if (STRCMP(eap->arg, "off") == 0)
6615 syn_time_on = FALSE;
6616 else if (STRCMP(eap->arg, "clear") == 0)
6617 syntime_clear();
6618 else if (STRCMP(eap->arg, "report") == 0)
6619 syntime_report();
6620 else
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00006621 semsg(_(e_invalid_argument_str), eap->arg);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006622}
6623
6624 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006625syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006626{
6627 profile_zero(&st->total);
6628 profile_zero(&st->slowest);
6629 st->count = 0;
6630 st->match = 0;
6631}
6632
6633/*
6634 * Clear the syntax timing for the current buffer.
6635 */
6636 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006637syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006638{
6639 int idx;
6640 synpat_T *spp;
6641
6642 if (!syntax_present(curwin))
6643 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006644 msg(_(msg_no_items));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006645 return;
6646 }
6647 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6648 {
6649 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6650 syn_clear_time(&spp->sp_time);
6651 }
6652}
6653
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006654/*
6655 * Function given to ExpandGeneric() to obtain the possible arguments of the
6656 * ":syntime {on,off,clear,report}" command.
6657 */
6658 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006659get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006660{
6661 switch (idx)
6662 {
6663 case 0: return (char_u *)"on";
6664 case 1: return (char_u *)"off";
6665 case 2: return (char_u *)"clear";
6666 case 3: return (char_u *)"report";
6667 }
6668 return NULL;
6669}
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006670
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006671typedef struct
6672{
6673 proftime_T total;
6674 int count;
6675 int match;
6676 proftime_T slowest;
6677 proftime_T average;
6678 int id;
6679 char_u *pattern;
6680} time_entry_T;
6681
6682 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006683syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006684{
6685 const time_entry_T *s1 = v1;
6686 const time_entry_T *s2 = v2;
6687
6688 return profile_cmp(&s1->total, &s2->total);
6689}
6690
6691/*
6692 * Clear the syntax timing for the current buffer.
6693 */
6694 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006695syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006696{
6697 int idx;
6698 synpat_T *spp;
Bram Moolenaar73e28dc2022-09-17 21:08:33 +01006699# if defined(FEAT_RELTIME)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006700 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006701# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006702 int len;
6703 proftime_T total_total;
6704 int total_count = 0;
6705 garray_T ga;
6706 time_entry_T *p;
6707
6708 if (!syntax_present(curwin))
6709 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006710 msg(_(msg_no_items));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006711 return;
6712 }
6713
6714 ga_init2(&ga, sizeof(time_entry_T), 50);
6715 profile_zero(&total_total);
6716 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6717 {
6718 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6719 if (spp->sp_time.count > 0)
6720 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006721 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006722 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6723 p->total = spp->sp_time.total;
6724 profile_add(&total_total, &spp->sp_time.total);
6725 p->count = spp->sp_time.count;
6726 p->match = spp->sp_time.match;
6727 total_count += spp->sp_time.count;
6728 p->slowest = spp->sp_time.slowest;
Bram Moolenaar73e28dc2022-09-17 21:08:33 +01006729# if defined(FEAT_RELTIME)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006730 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6731 p->average = tm;
6732# endif
6733 p->id = spp->sp_syn.id;
6734 p->pattern = spp->sp_pattern;
6735 ++ga.ga_len;
6736 }
6737 }
6738
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006739 // Sort on total time. Skip if there are no items to avoid passing NULL
6740 // pointer to qsort().
Bram Moolenaara2162552017-01-08 17:46:20 +01006741 if (ga.ga_len > 1)
6742 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006743 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006744
Bram Moolenaar32526b32019-01-19 17:43:09 +01006745 msg_puts_title(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6746 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006747 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6748 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006749 p = ((time_entry_T *)ga.ga_data) + idx;
6750
Bram Moolenaar32526b32019-01-19 17:43:09 +01006751 msg_puts(profile_msg(&p->total));
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006752 msg_puts(" "); // make sure there is always a separating space
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006753 msg_advance(13);
6754 msg_outnum(p->count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006755 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006756 msg_advance(20);
6757 msg_outnum(p->match);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006758 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006759 msg_advance(26);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006760 msg_puts(profile_msg(&p->slowest));
6761 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006762 msg_advance(38);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006763 msg_puts(profile_msg(&p->average));
6764 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006765 msg_advance(50);
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02006766 msg_outtrans(highlight_group_name(p->id - 1));
Bram Moolenaar32526b32019-01-19 17:43:09 +01006767 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006768
6769 msg_advance(69);
6770 if (Columns < 80)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006771 len = 20; // will wrap anyway
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006772 else
6773 len = Columns - 70;
6774 if (len > (int)STRLEN(p->pattern))
6775 len = (int)STRLEN(p->pattern);
6776 msg_outtrans_len(p->pattern, len);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006777 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006778 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006779 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006780 if (!got_int)
6781 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006782 msg_puts("\n");
6783 msg_puts(profile_msg(&total_total));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006784 msg_advance(13);
6785 msg_outnum(total_count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006786 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006787 }
6788}
6789#endif
6790
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006791#endif // FEAT_SYN_HL