blob: 34563aad9dbe643e783fe10c38a69a7f34fe1f14 [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// different types of offsets that are possible
19#define SPO_MS_OFF 0 // match start offset
20#define SPO_ME_OFF 1 // match end offset
21#define SPO_HS_OFF 2 // highl. start offset
22#define SPO_HE_OFF 3 // highl. end offset
23#define SPO_RS_OFF 4 // region start offset
24#define SPO_RE_OFF 5 // region end offset
25#define SPO_LC_OFF 6 // leading context offset
Bram Moolenaar071d4272004-06-13 20:20:40 +000026#define SPO_COUNT 7
27
28static char *(spo_name_tab[SPO_COUNT]) =
29 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
30
31/*
32 * The patterns that are being searched for are stored in a syn_pattern.
33 * A match item consists of one pattern.
34 * A start/end item consists of n start patterns and m end patterns.
35 * A start/skip/end item consists of n start patterns, one skip pattern and m
36 * end patterns.
37 * For the latter two, the patterns are always consecutive: start-skip-end.
38 *
39 * A character offset can be given for the matched text (_m_start and _m_end)
40 * and for the actually highlighted text (_h_start and _h_end).
Bram Moolenaar36f92302018-02-24 21:36:34 +010041 *
42 * Note that ordering of members is optimized to reduce padding.
Bram Moolenaar071d4272004-06-13 20:20:40 +000043 */
44typedef struct syn_pattern
45{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010046 char sp_type; // see SPTYPE_ defines below
47 char sp_syncing; // this item used for syncing
48 short sp_syn_match_id; // highlight group ID of pattern
49 short sp_off_flags; // see below
50 int sp_offsets[SPO_COUNT]; // offsets
51 int sp_flags; // see HL_ defines below
Bram Moolenaar860cae12010-06-05 23:22:07 +020052#ifdef FEAT_CONCEAL
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010053 int sp_cchar; // conceal substitute character
Bram Moolenaar860cae12010-06-05 23:22:07 +020054#endif
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010055 int sp_ic; // ignore-case flag for sp_prog
56 int sp_sync_idx; // sync item index (syncing only)
57 int sp_line_id; // ID of last line where tried
58 int sp_startcol; // next match in sp_line_id line
59 short *sp_cont_list; // cont. group IDs, if non-zero
60 short *sp_next_list; // next group IDs, if non-zero
61 struct sp_syn sp_syn; // struct passed to in_id_list()
62 char_u *sp_pattern; // regexp to match, pattern
63 regprog_T *sp_prog; // regexp to match, program
Bram Moolenaarf7512552013-06-06 14:55:19 +020064#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +020065 syn_time_T sp_time;
66#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000067} synpat_T;
68
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010069// The sp_off_flags are computed like this:
70// offset from the start of the matched text: (1 << SPO_XX_OFF)
71// offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
72// When both are present, only one is used.
Bram Moolenaar071d4272004-06-13 20:20:40 +000073
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010074#define SPTYPE_MATCH 1 // match keyword with this group ID
75#define SPTYPE_START 2 // match a regexp, start of item
76#define SPTYPE_END 3 // match a regexp, end of item
77#define SPTYPE_SKIP 4 // match a regexp, skip within item
Bram Moolenaar071d4272004-06-13 20:20:40 +000078
Bram Moolenaar071d4272004-06-13 20:20:40 +000079
80#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
81
kylo252ae6f1d82022-02-16 19:24:07 +000082#define NONE_IDX (-2) // value of sp_sync_idx for "NONE"
Bram Moolenaar071d4272004-06-13 20:20:40 +000083
84/*
85 * Flags for b_syn_sync_flags:
86 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010087#define SF_CCOMMENT 0x01 // sync on a C-style comment
88#define SF_MATCH 0x02 // sync by matching a pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +000089
90#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
91
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010092#define MAXKEYWLEN 80 // maximum length of a keyword
Bram Moolenaar071d4272004-06-13 20:20:40 +000093
94/*
95 * The attributes of the syntax item that has been recognized.
96 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010097static int current_attr = 0; // attr of current syntax word
Bram Moolenaar071d4272004-06-13 20:20:40 +000098#ifdef FEAT_EVAL
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +010099static int current_id = 0; // ID of current char for syn_get_id()
100static int current_trans_id = 0; // idem, transparency removed
Bram Moolenaar071d4272004-06-13 20:20:40 +0000101#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200102#ifdef FEAT_CONCEAL
103static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200104static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200105static int current_sub_char = 0;
106#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000107
Bram Moolenaar217ad922005-03-20 22:37:15 +0000108typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000109{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100110 char_u *scl_name; // syntax cluster name
111 char_u *scl_name_u; // uppercase of scl_name
112 short *scl_list; // IDs in this syntax cluster
Bram Moolenaar217ad922005-03-20 22:37:15 +0000113} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000114
115/*
116 * Methods of combining two clusters
117 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100118#define CLUSTER_REPLACE 1 // replace first list with second
119#define CLUSTER_ADD 2 // add second list to first
120#define CLUSTER_SUBTRACT 3 // subtract second list from first
Bram Moolenaar071d4272004-06-13 20:20:40 +0000121
Bram Moolenaar217ad922005-03-20 22:37:15 +0000122#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000123
124/*
125 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200126 * 0 - 19999 normal syntax groups
127 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
128 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
129 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
130 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000131 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100132#define SYNID_ALLBUT MAX_HL_ID // syntax group ID for contains=ALLBUT
133#define SYNID_TOP 21000 // syntax group ID for contains=TOP
134#define SYNID_CONTAINED 22000 // syntax group ID for contains=CONTAINED
135#define SYNID_CLUSTER 23000 // first syntax group ID for clusters
Bram Moolenaar42431a72011-04-01 14:44:59 +0200136
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100137#define MAX_SYN_INC_TAG 999 // maximum before the above overflow
Bram Moolenaar42431a72011-04-01 14:44:59 +0200138#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000139
140/*
141 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
142 * expand_filename(). Most of the other syntax commands don't need it, so
143 * instead of passing it to them, we stow it here.
144 */
145static char_u **syn_cmdlinep;
146
147/*
148 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200149 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000150 * rules in each ":syn include"'d file.
151 */
152static int current_syn_inc_tag = 0;
153static int running_syn_inc_tag = 0;
154
155/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000156 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
157 * This avoids adding a pointer to the hashtable item.
158 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
159 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
160 * HI2KE() converts a hashitem pointer to a var pointer.
161 */
162static keyentry_T dumkey;
163#define KE2HIKEY(kp) ((kp)->keyword)
164#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
165#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
166
167/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000168 * To reduce the time spent in keepend(), remember at which level in the state
169 * stack the first item with "keepend" is present. When "-1", there is no
170 * "keepend" on the stack.
171 */
172static int keepend_level = -1;
173
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200174static char msg_no_items[] = N_("No Syntax items defined for this buffer");
175
Bram Moolenaar071d4272004-06-13 20:20:40 +0000176/*
177 * For the current state we need to remember more than just the idx.
178 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
179 * (The end positions have the column number of the next char)
180 */
181typedef struct state_item
182{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100183 int si_idx; // index of syntax pattern or
184 // KEYWORD_IDX
185 int si_id; // highlight group ID for keywords
186 int si_trans_id; // idem, transparency removed
187 int si_m_lnum; // lnum of the match
188 int si_m_startcol; // starting column of the match
189 lpos_T si_m_endpos; // just after end posn of the match
190 lpos_T si_h_startpos; // start position of the highlighting
191 lpos_T si_h_endpos; // end position of the highlighting
192 lpos_T si_eoe_pos; // end position of end pattern
193 int si_end_idx; // group ID for end pattern or zero
194 int si_ends; // if match ends before si_m_endpos
195 int si_attr; // attributes in this state
196 long si_flags; // HL_HAS_EOL flag in this state, and
197 // HL_SKIP* for si_next_list
Bram Moolenaar860cae12010-06-05 23:22:07 +0200198#ifdef FEAT_CONCEAL
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100199 int si_seqnr; // sequence number
200 int si_cchar; // substitution character for conceal
Bram Moolenaar860cae12010-06-05 23:22:07 +0200201#endif
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100202 short *si_cont_list; // list of contained groups
203 short *si_next_list; // nextgroup IDs after this item ends
204 reg_extmatch_T *si_extmatch; // \z(...\) matches from start
205 // pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +0000206} stateitem_T;
207
kylo252ae6f1d82022-02-16 19:24:07 +0000208#define KEYWORD_IDX (-1) // value of si_idx for keywords
209#define ID_LIST_ALL ((short *)-1) // valid of si_cont_list for containing all
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100210 // but contained groups
Bram Moolenaar071d4272004-06-13 20:20:40 +0000211
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200212#ifdef FEAT_CONCEAL
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100213static int next_seqnr = 1; // value to use for si_seqnr
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200214#endif
215
Bram Moolenaar071d4272004-06-13 20:20:40 +0000216/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000217 * Struct to reduce the number of arguments to get_syn_options(), it's used
218 * very often.
219 */
220typedef struct
221{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100222 int flags; // flags for contained and transparent
223 int keyword; // TRUE for ":syn keyword"
224 int *sync_idx; // syntax item for "grouphere" argument, NULL
225 // if not allowed
226 char has_cont_list; // TRUE if "cont_list" can be used
227 short *cont_list; // group IDs for "contains" argument
228 short *cont_in_list; // group IDs for "containedin" argument
229 short *next_list; // group IDs for "nextgroup" argument
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000230} syn_opt_arg_T;
231
232/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000233 * The next possible match in the current line for any pattern is remembered,
234 * to avoid having to try for a match in each column.
235 * If next_match_idx == -1, not tried (in this line) yet.
236 * If next_match_col == MAXCOL, no match found in this line.
237 * (All end positions have the column of the char after the end)
238 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100239static int next_match_col; // column for start of next match
240static lpos_T next_match_m_endpos; // position for end of next match
241static lpos_T next_match_h_startpos; // pos. for highl. start of next match
242static lpos_T next_match_h_endpos; // pos. for highl. end of next match
243static int next_match_idx; // index of matched item
244static long next_match_flags; // flags for next match
245static lpos_T next_match_eos_pos; // end of start pattn (start region)
246static lpos_T next_match_eoe_pos; // pos. for end of end pattern
247static int next_match_end_idx; // ID of group for end pattn or zero
Bram Moolenaar071d4272004-06-13 20:20:40 +0000248static reg_extmatch_T *next_match_extmatch = NULL;
249
250/*
251 * A state stack is an array of integers or stateitem_T, stored in a
Bram Moolenaarf86db782018-10-25 13:31:37 +0200252 * garray_T. A state stack is invalid if its itemsize entry is zero.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000253 */
254#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
255#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
256
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200257#define FOR_ALL_SYNSTATES(sb, sst) \
258 for ((sst) = (sb)->b_sst_first; (sst) != NULL; (sst) = (sst)->sst_next)
259
Bram Moolenaar071d4272004-06-13 20:20:40 +0000260/*
261 * The current state (within the line) of the recognition engine.
262 * When current_state.ga_itemsize is 0 the current state is invalid.
263 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100264static win_T *syn_win; // current window for highlighting
265static buf_T *syn_buf; // current buffer for highlighting
266static synblock_T *syn_block; // current buffer for highlighting
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100267static linenr_T current_lnum = 0; // lnum of current state
268static colnr_T current_col = 0; // column of current state
269static int current_state_stored = 0; // TRUE if stored current state
270 // after setting current_finished
271static int current_finished = 0; // current line has been finished
272static garray_T current_state // current stack of state_items
Bram Moolenaar071d4272004-06-13 20:20:40 +0000273 = {0, 0, 0, 0, NULL};
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100274static short *current_next_list = NULL; // when non-zero, nextgroup list
275static int current_next_flags = 0; // flags for current_next_list
276static int current_line_id = 0; // unique number for current line
Bram Moolenaar071d4272004-06-13 20:20:40 +0000277
278#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
279
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100280static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100281static int syn_match_linecont(linenr_T lnum);
282static void syn_start_line(void);
283static void syn_update_ends(int startofline);
284static void syn_stack_alloc(void);
285static int syn_stack_cleanup(void);
286static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
287static synstate_T *syn_stack_find_entry(linenr_T lnum);
288static synstate_T *store_current_state(void);
289static void load_current_state(synstate_T *from);
290static void invalidate_current_state(void);
291static int syn_stack_equal(synstate_T *sp);
292static void validate_current_state(void);
293static int syn_finish_line(int syncing);
294static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
295static int did_match_already(int idx, garray_T *gap);
296static stateitem_T *push_next_match(stateitem_T *cur_si);
297static void check_state_ends(void);
298static void update_si_attr(int idx);
299static void check_keepend(void);
300static void update_si_end(stateitem_T *sip, int startcol, int force);
301static short *copy_id_list(short *list);
Theodore Duboisf50d5362025-02-05 23:59:25 +0100302static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int flags);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100303static int push_current_state(int idx);
304static void pop_current_state(void);
Bram Moolenaarf7512552013-06-06 14:55:19 +0200305#ifdef FEAT_PROFILE
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100306static void syn_clear_time(syn_time_T *tt);
307static void syntime_clear(void);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100308static void syntime_report(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200309static int syn_time_on = FALSE;
310# define IF_SYN_TIME(p) (p)
311#else
312# define IF_SYN_TIME(p) NULL
313typedef int syn_time_T;
314#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000315
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100316static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
317static 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 +0000318
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100319static void limit_pos(lpos_T *pos, lpos_T *limit);
320static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
321static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
322static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
323static char_u *syn_getcurline(void);
John Marriottb4ea7712024-08-01 23:01:25 +0200324static colnr_T syn_getcurline_len(void);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100325static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
326static 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 +0100327static void syn_remove_pattern(synblock_T *block, int idx);
328static void syn_clear_pattern(synblock_T *block, int i);
329static void syn_clear_cluster(synblock_T *block, int i);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100330static void syn_clear_one(int id, int syncing);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100331static void syn_cmd_onoff(exarg_T *eap, char *name);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100332static void syn_lines_msg(void);
333static void syn_match_msg(void);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100334static void syn_list_one(int id, int syncing, int link_only);
335static void syn_list_cluster(int id);
336static void put_id_list(char_u *name, short *list, int attr);
337static void put_pattern(char *s, int c, synpat_T *spp, int attr);
338static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
339static void syn_clear_keyword(int id, hashtab_T *ht);
340static void clear_keywtab(hashtab_T *ht);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100341static int syn_scl_namen2id(char_u *linep, int len);
342static int syn_check_cluster(char_u *pp, int len);
343static int syn_add_cluster(char_u *name);
344static void init_syn_patterns(void);
345static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100346static int get_id_list(char_u **arg, int keylen, short **list, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100347static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000348
349/*
350 * Start the syntax recognition for a line. This function is normally called
351 * from the screen updating, once for each displayed line.
352 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
353 * it. Careful: curbuf and curwin are likely to point to another buffer and
354 * window.
355 */
356 void
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200357syntax_start(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000358{
359 synstate_T *p;
360 synstate_T *last_valid = NULL;
361 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000362 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000363 linenr_T parsed_lnum;
364 linenr_T first_stored;
365 int dist;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100366 static varnumber_T changedtick = 0; // remember the last change ID
Bram Moolenaar071d4272004-06-13 20:20:40 +0000367
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200368#ifdef FEAT_CONCEAL
369 current_sub_char = NUL;
370#endif
371
Bram Moolenaar071d4272004-06-13 20:20:40 +0000372 /*
373 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000374 * Also do this when a change was made, the current state may be invalid
375 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000376 */
Bram Moolenaarb681be12016-03-31 23:02:16 +0200377 if (syn_block != wp->w_s
378 || syn_buf != wp->w_buffer
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100379 || changedtick != CHANGEDTICK(syn_buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000380 {
381 invalidate_current_state();
382 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200383 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000384 }
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100385 changedtick = CHANGEDTICK(syn_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000386 syn_win = wp;
387
388 /*
389 * Allocate syntax stack when needed.
390 */
391 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200392 if (syn_block->b_sst_array == NULL)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100393 return; // out of memory
Bram Moolenaar860cae12010-06-05 23:22:07 +0200394 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000395
396 /*
397 * If the state of the end of the previous line is useful, store it.
398 */
399 if (VALID_STATE(&current_state)
400 && current_lnum < lnum
401 && current_lnum < syn_buf->b_ml.ml_line_count)
402 {
403 (void)syn_finish_line(FALSE);
404 if (!current_state_stored)
405 {
406 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000407 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000408 }
409
410 /*
411 * If the current_lnum is now the same as "lnum", keep the current
412 * state (this happens very often!). Otherwise invalidate
413 * current_state and figure it out below.
414 */
415 if (current_lnum != lnum)
416 invalidate_current_state();
417 }
418 else
419 invalidate_current_state();
420
421 /*
422 * Try to synchronize from a saved state in b_sst_array[].
423 * Only do this if lnum is not before and not to far beyond a saved state.
424 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200425 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000426 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100427 // Find last valid saved state before start_lnum.
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200428 FOR_ALL_SYNSTATES(syn_block, p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000429 {
430 if (p->sst_lnum > lnum)
431 break;
432 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
433 {
434 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200435 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000436 last_min_valid = p;
437 }
438 }
439 if (last_min_valid != NULL)
440 load_current_state(last_min_valid);
441 }
442
443 /*
444 * If "lnum" is before or far beyond a line with a saved state, need to
445 * re-synchronize.
446 */
447 if (INVALID_STATE(&current_state))
448 {
449 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200450 if (current_lnum == 1)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100451 // First line is always valid, no matter "minlines".
Bram Moolenaard6761c32011-06-19 04:54:21 +0200452 first_stored = 1;
453 else
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100454 // Need to parse "minlines" lines before state can be considered
455 // valid to store.
Bram Moolenaard6761c32011-06-19 04:54:21 +0200456 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000457 }
458 else
459 first_stored = current_lnum;
460
461 /*
462 * Advance from the sync point or saved state until the current line.
463 * Save some entries for syncing with later on.
464 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200465 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000466 dist = 999999;
467 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200468 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000469 while (current_lnum < lnum)
470 {
471 syn_start_line();
472 (void)syn_finish_line(FALSE);
473 ++current_lnum;
474
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100475 // If we parsed at least "minlines" lines or started at a valid
476 // state, the current state is considered valid.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000477 if (current_lnum >= first_stored)
478 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100479 // Check if the saved state entry is for the current line and is
480 // equal to the current state. If so, then validate all saved
481 // states that depended on a change before the parsed line.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000482 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000483 prev = syn_stack_find_entry(current_lnum - 1);
484 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200485 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000486 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000487 sp = prev;
488 while (sp != NULL && sp->sst_lnum < current_lnum)
489 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000490 if (sp != NULL
491 && sp->sst_lnum == current_lnum
492 && syn_stack_equal(sp))
493 {
494 parsed_lnum = current_lnum;
495 prev = sp;
496 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
497 {
498 if (sp->sst_lnum <= lnum)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100499 // valid state before desired line, use this one
Bram Moolenaar071d4272004-06-13 20:20:40 +0000500 prev = sp;
501 else if (sp->sst_change_lnum == 0)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100502 // past saved states depending on change, break here.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000503 break;
504 sp->sst_change_lnum = 0;
505 sp = sp->sst_next;
506 }
507 load_current_state(prev);
508 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100509 // Store the state at this line when it's the first one, the line
510 // where we start parsing, or some distance from the previously
511 // saved state. But only when parsed at least 'minlines'.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000512 else if (prev == NULL
513 || current_lnum == lnum
514 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000515 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000516 }
517
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100518 // This can take a long time: break when CTRL-C pressed. The current
519 // state will be wrong then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000520 line_breakcheck();
521 if (got_int)
522 {
523 current_lnum = lnum;
524 break;
525 }
526 }
527
528 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000529}
530
531/*
532 * We cannot simply discard growarrays full of state_items or buf_states; we
533 * have to manually release their extmatch pointers first.
534 */
535 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100536clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000537{
538 int i;
539 garray_T *gap;
540
541 if (p->sst_stacksize > SST_FIX_STATES)
542 {
543 gap = &(p->sst_union.sst_ga);
544 for (i = 0; i < gap->ga_len; i++)
545 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
546 ga_clear(gap);
547 }
548 else
549 {
550 for (i = 0; i < p->sst_stacksize; i++)
551 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
552 }
553}
554
555/*
556 * Cleanup the current_state stack.
557 */
558 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100559clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000560{
561 int i;
562 stateitem_T *sip;
563
564 sip = (stateitem_T *)(current_state.ga_data);
565 for (i = 0; i < current_state.ga_len; i++)
566 unref_extmatch(sip[i].si_extmatch);
567 ga_clear(&current_state);
568}
569
570/*
571 * Try to find a synchronisation point for line "lnum".
572 *
573 * This sets current_lnum and the current state. One of three methods is
574 * used:
575 * 1. Search backwards for the end of a C-comment.
576 * 2. Search backwards for given sync patterns.
577 * 3. Simply start on a given number of lines above "lnum".
578 */
579 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100580syn_sync(
581 win_T *wp,
582 linenr_T start_lnum,
583 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000584{
585 buf_T *curbuf_save;
586 win_T *curwin_save;
587 pos_T cursor_save;
588 int idx;
589 linenr_T lnum;
590 linenr_T end_lnum;
591 linenr_T break_lnum;
592 int had_sync_point;
593 stateitem_T *cur_si;
594 synpat_T *spp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000595 int found_flags = 0;
596 int found_match_idx = 0;
597 linenr_T found_current_lnum = 0;
598 int found_current_col= 0;
599 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000600 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000601
602 /*
603 * Clear any current state that might be hanging around.
604 */
605 invalidate_current_state();
606
607 /*
608 * Start at least "minlines" back. Default starting point for parsing is
609 * there.
610 * Start further back, to avoid that scrolling backwards will result in
611 * resyncing for every line. Now it resyncs only one out of N lines,
612 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
613 * Watch out for overflow when minlines is MAXLNUM.
614 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200615 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000616 start_lnum = 1;
617 else
618 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200619 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000620 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200621 else if (syn_block->b_syn_sync_minlines < 10)
622 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000623 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200624 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
625 if (syn_block->b_syn_sync_maxlines != 0
626 && lnum > syn_block->b_syn_sync_maxlines)
627 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000628 if (lnum >= start_lnum)
629 start_lnum = 1;
630 else
631 start_lnum -= lnum;
632 }
633 current_lnum = start_lnum;
634
635 /*
636 * 1. Search backwards for the end of a C-style comment.
637 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200638 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000639 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100640 // Need to make syn_buf the current buffer for a moment, to be able to
641 // use find_start_comment().
Bram Moolenaar071d4272004-06-13 20:20:40 +0000642 curwin_save = curwin;
643 curwin = wp;
644 curbuf_save = curbuf;
645 curbuf = syn_buf;
646
647 /*
648 * Skip lines that end in a backslash.
649 */
650 for ( ; start_lnum > 1; --start_lnum)
651 {
zeertzjq94b7c322024-03-12 21:50:32 +0100652 char_u *l = ml_get(start_lnum - 1);
653
654 if (*l == NUL || *(l + ml_get_len(start_lnum - 1) - 1) != '\\')
Bram Moolenaar071d4272004-06-13 20:20:40 +0000655 break;
656 }
657 current_lnum = start_lnum;
658
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100659 // set cursor to start of search
Bram Moolenaar071d4272004-06-13 20:20:40 +0000660 cursor_save = wp->w_cursor;
661 wp->w_cursor.lnum = start_lnum;
662 wp->w_cursor.col = 0;
663
664 /*
665 * If the line is inside a comment, need to find the syntax item that
666 * defines the comment.
667 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
668 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200669 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000670 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200671 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
672 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
673 == syn_block->b_syn_sync_id
674 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000675 {
676 validate_current_state();
677 if (push_current_state(idx) == OK)
678 update_si_attr(current_state.ga_len - 1);
679 break;
680 }
681 }
682
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100683 // restore cursor and buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +0000684 wp->w_cursor = cursor_save;
685 curwin = curwin_save;
686 curbuf = curbuf_save;
687 }
688
689 /*
690 * 2. Search backwards for given sync patterns.
691 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200692 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000693 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200694 if (syn_block->b_syn_sync_maxlines != 0
695 && start_lnum > syn_block->b_syn_sync_maxlines)
696 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000697 else
698 break_lnum = 0;
699
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000700 found_m_endpos.lnum = 0;
701 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000702 end_lnum = start_lnum;
703 lnum = start_lnum;
704 while (--lnum > break_lnum)
705 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100706 // This can take a long time: break when CTRL-C pressed.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000707 line_breakcheck();
708 if (got_int)
709 {
710 invalidate_current_state();
711 current_lnum = start_lnum;
712 break;
713 }
714
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100715 // Check if we have run into a valid saved state stack now.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000716 if (last_valid != NULL && lnum == last_valid->sst_lnum)
717 {
718 load_current_state(last_valid);
719 break;
720 }
721
722 /*
723 * Check if the previous line has the line-continuation pattern.
724 */
725 if (lnum > 1 && syn_match_linecont(lnum - 1))
726 continue;
727
728 /*
729 * Start with nothing on the state stack
730 */
731 validate_current_state();
732
733 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
734 {
735 syn_start_line();
736 for (;;)
737 {
738 had_sync_point = syn_finish_line(TRUE);
739 /*
740 * When a sync point has been found, remember where, and
741 * continue to look for another one, further on in the line.
742 */
743 if (had_sync_point && current_state.ga_len)
744 {
745 cur_si = &CUR_STATE(current_state.ga_len - 1);
746 if (cur_si->si_m_endpos.lnum > start_lnum)
747 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100748 // ignore match that goes to after where started
Bram Moolenaar071d4272004-06-13 20:20:40 +0000749 current_lnum = end_lnum;
750 break;
751 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000752 if (cur_si->si_idx < 0)
753 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100754 // Cannot happen?
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000755 found_flags = 0;
756 found_match_idx = KEYWORD_IDX;
757 }
758 else
759 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200760 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000761 found_flags = spp->sp_flags;
762 found_match_idx = spp->sp_sync_idx;
763 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000764 found_current_lnum = current_lnum;
765 found_current_col = current_col;
766 found_m_endpos = cur_si->si_m_endpos;
767 /*
768 * Continue after the match (be aware of a zero-length
769 * match).
770 */
771 if (found_m_endpos.lnum > current_lnum)
772 {
773 current_lnum = found_m_endpos.lnum;
774 current_col = found_m_endpos.col;
775 if (current_lnum >= end_lnum)
776 break;
777 }
778 else if (found_m_endpos.col > current_col)
779 current_col = found_m_endpos.col;
780 else
781 ++current_col;
782
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100783 // syn_current_attr() will have skipped the check for
784 // an item that ends here, need to do that now. Be
785 // careful not to go past the NUL.
Bram Moolenaar81366db2005-07-24 21:16:51 +0000786 prev_current_col = current_col;
787 if (syn_getcurline()[current_col] != NUL)
788 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000789 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000790 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000791 }
792 else
793 break;
794 }
795 }
796
797 /*
798 * If a sync point was encountered, break here.
799 */
800 if (found_flags)
801 {
802 /*
803 * Put the item that was specified by the sync point on the
804 * state stack. If there was no item specified, make the
805 * state stack empty.
806 */
807 clear_current_state();
808 if (found_match_idx >= 0
809 && push_current_state(found_match_idx) == OK)
810 update_si_attr(current_state.ga_len - 1);
811
812 /*
813 * When using "grouphere", continue from the sync point
814 * match, until the end of the line. Parsing starts at
815 * the next line.
816 * For "groupthere" the parsing starts at start_lnum.
817 */
818 if (found_flags & HL_SYNC_HERE)
819 {
820 if (current_state.ga_len)
821 {
822 cur_si = &CUR_STATE(current_state.ga_len - 1);
823 cur_si->si_h_startpos.lnum = found_current_lnum;
824 cur_si->si_h_startpos.col = found_current_col;
825 update_si_end(cur_si, (int)current_col, TRUE);
826 check_keepend();
827 }
828 current_col = found_m_endpos.col;
829 current_lnum = found_m_endpos.lnum;
830 (void)syn_finish_line(FALSE);
831 ++current_lnum;
832 }
833 else
834 current_lnum = start_lnum;
835
836 break;
837 }
838
839 end_lnum = lnum;
840 invalidate_current_state();
841 }
842
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100843 // Ran into start of the file or exceeded maximum number of lines
Bram Moolenaar071d4272004-06-13 20:20:40 +0000844 if (lnum <= break_lnum)
845 {
846 invalidate_current_state();
847 current_lnum = break_lnum + 1;
848 }
849 }
850
851 validate_current_state();
852}
853
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100854 static void
855save_chartab(char_u *chartab)
856{
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000857 if (syn_block->b_syn_isk == empty_option)
858 return;
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, (size_t)32);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100862}
863
864 static void
865restore_chartab(char_u *chartab)
866{
867 if (syn_win->w_s->b_syn_isk != empty_option)
868 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
869}
870
Bram Moolenaar071d4272004-06-13 20:20:40 +0000871/*
872 * Return TRUE if the line-continuation pattern matches in line "lnum".
873 */
874 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100875syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000876{
877 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100878 int r;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100879 char_u buf_chartab[32]; // chartab array for syn iskyeyword
Bram Moolenaar071d4272004-06-13 20:20:40 +0000880
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000881 if (syn_block->b_syn_linecont_prog == NULL)
882 return FALSE;
883
884 // use syntax iskeyword option
885 save_chartab(buf_chartab);
886 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
887 regmatch.regprog = syn_block->b_syn_linecont_prog;
888 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
889 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
890 syn_block->b_syn_linecont_prog = regmatch.regprog;
891 restore_chartab(buf_chartab);
892 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000893}
894
895/*
896 * Prepare the current state for the start of a line.
897 */
898 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100899syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000900{
901 current_finished = FALSE;
902 current_col = 0;
903
904 /*
905 * Need to update the end of a start/skip/end that continues from the
906 * previous line and regions that have "keepend".
907 */
908 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +0200909 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000910 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +0200911 check_state_ends();
912 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000913
914 next_match_idx = -1;
915 ++current_line_id;
Bram Moolenaarea20de82017-06-24 22:52:24 +0200916#ifdef FEAT_CONCEAL
Bram Moolenaarcc0750d2017-06-24 22:29:24 +0200917 next_seqnr = 1;
Bram Moolenaarea20de82017-06-24 22:52:24 +0200918#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000919}
920
921/*
922 * Check for items in the stack that need their end updated.
923 * When "startofline" is TRUE the last item is always updated.
924 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
925 */
926 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100927syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000928{
929 stateitem_T *cur_si;
930 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000931 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000932
933 if (startofline)
934 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100935 // Check for a match carried over from a previous line with a
936 // contained region. The match ends as soon as the region ends.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000937 for (i = 0; i < current_state.ga_len; ++i)
938 {
939 cur_si = &CUR_STATE(i);
940 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +0200941 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +0000942 == SPTYPE_MATCH
943 && cur_si->si_m_endpos.lnum < current_lnum)
944 {
945 cur_si->si_flags |= HL_MATCHCONT;
946 cur_si->si_m_endpos.lnum = 0;
947 cur_si->si_m_endpos.col = 0;
948 cur_si->si_h_endpos = cur_si->si_m_endpos;
949 cur_si->si_ends = TRUE;
950 }
951 }
952 }
953
954 /*
955 * Need to update the end of a start/skip/end that continues from the
956 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000957 * influence contained items. If we've just removed "extend"
958 * (startofline == 0) then we should update ends of normal regions
959 * contained inside "keepend" because "extend" could have extended
960 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000961 * Then check for items ending in column 0.
962 */
963 i = current_state.ga_len - 1;
964 if (keepend_level >= 0)
965 for ( ; i > keepend_level; --i)
966 if (CUR_STATE(i).si_flags & HL_EXTEND)
967 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000968
969 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000970 for ( ; i < current_state.ga_len; ++i)
971 {
972 cur_si = &CUR_STATE(i);
973 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000974 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000975 || (i == current_state.ga_len - 1 && startofline))
976 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100977 cur_si->si_h_startpos.col = 0; // start highl. in col 0
Bram Moolenaar071d4272004-06-13 20:20:40 +0000978 cur_si->si_h_startpos.lnum = current_lnum;
979
980 if (!(cur_si->si_flags & HL_MATCHCONT))
981 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000982
983 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
984 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000985 }
986 }
987 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000988}
989
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +0100990/////////////////////////////////////////
991// Handling of the state stack cache.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000992
993/*
994 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
995 *
996 * To speed up syntax highlighting, the state stack for the start of some
997 * lines is cached. These entries can be used to start parsing at that point.
998 *
999 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1000 * valid entries. b_sst_first points to the first one, then follow sst_next.
1001 * The entries are sorted on line number. The first entry is often for line 2
1002 * (line 1 always starts with an empty stack).
1003 * There is also a list for free entries. This construction is used to avoid
1004 * having to allocate and free memory blocks too often.
1005 *
1006 * When making changes to the buffer, this is logged in b_mod_*. When calling
1007 * update_screen() to update the display, it will call
1008 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1009 * entries. The entries which are inside the changed area are removed,
1010 * because they must be recomputed. Entries below the changed have their line
1011 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1012 * set to indicate that a check must be made if the changed lines would change
1013 * the cached entry.
1014 *
1015 * When later displaying lines, an entry is stored for each line. Displayed
1016 * lines are likely to be displayed again, in which case the state at the
1017 * start of the line is needed.
1018 * For not displayed lines, an entry is stored for every so many lines. These
1019 * entries will be used e.g., when scrolling backwards. The distance between
1020 * entries depends on the number of lines in the buffer. For small buffers
1021 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1022 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1023 */
1024
Bram Moolenaar860cae12010-06-05 23:22:07 +02001025 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001026syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001027{
1028 synstate_T *p;
1029
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00001030 if (block->b_sst_array == NULL)
1031 return;
1032
1033 FOR_ALL_SYNSTATES(block, p)
1034 clear_syn_state(p);
1035 VIM_CLEAR(block->b_sst_array);
1036 block->b_sst_first = NULL;
1037 block->b_sst_len = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001038}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001039/*
1040 * Free b_sst_array[] for buffer "buf".
1041 * Used when syntax items changed to force resyncing everywhere.
1042 */
1043 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001044syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001045{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001046#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001047 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001048#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001049
Bram Moolenaar860cae12010-06-05 23:22:07 +02001050 syn_stack_free_block(block);
1051
Bram Moolenaar071d4272004-06-13 20:20:40 +00001052#ifdef FEAT_FOLDING
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001053 // When using "syntax" fold method, must update all folds.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001054 FOR_ALL_WINDOWS(wp)
1055 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001056 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001057 foldUpdateAll(wp);
1058 }
1059#endif
1060}
1061
1062/*
1063 * Allocate the syntax state stack for syn_buf when needed.
1064 * If the number of entries in b_sst_array[] is much too big or a bit too
1065 * small, reallocate it.
1066 * Also used to allocate b_sst_array[] for the first time.
1067 */
1068 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001069syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001070{
1071 long len;
1072 synstate_T *to, *from;
1073 synstate_T *sstp;
1074
1075 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1076 if (len < SST_MIN_ENTRIES)
1077 len = SST_MIN_ENTRIES;
1078 else if (len > SST_MAX_ENTRIES)
1079 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001080 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001081 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001082 // Allocate 50% too much, to avoid reallocating too often.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001083 len = syn_buf->b_ml.ml_line_count;
1084 len = (len + len / 2) / SST_DIST + Rows * 2;
1085 if (len < SST_MIN_ENTRIES)
1086 len = SST_MIN_ENTRIES;
1087 else if (len > SST_MAX_ENTRIES)
1088 len = SST_MAX_ENTRIES;
1089
Bram Moolenaar860cae12010-06-05 23:22:07 +02001090 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001091 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001092 // When shrinking the array, cleanup the existing stack.
1093 // Make sure that all valid entries fit in the new array.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001094 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001095 && syn_stack_cleanup())
1096 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001097 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1098 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001099 }
1100
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001101 sstp = ALLOC_CLEAR_MULT(synstate_T, len);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001102 if (sstp == NULL) // out of memory!
Bram Moolenaar071d4272004-06-13 20:20:40 +00001103 return;
1104
1105 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001106 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001107 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001108 // Move the states from the old array to the new one.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001109 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001110 from = from->sst_next)
1111 {
1112 ++to;
1113 *to = *from;
1114 to->sst_next = to + 1;
1115 }
1116 }
1117 if (to != sstp - 1)
1118 {
1119 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001120 syn_block->b_sst_first = sstp;
1121 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001122 }
1123 else
1124 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001125 syn_block->b_sst_first = NULL;
1126 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001127 }
1128
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001129 // Create the list of free entries.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001130 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001131 while (++to < sstp + len)
1132 to->sst_next = to + 1;
1133 (sstp + len - 1)->sst_next = NULL;
1134
Bram Moolenaar860cae12010-06-05 23:22:07 +02001135 vim_free(syn_block->b_sst_array);
1136 syn_block->b_sst_array = sstp;
1137 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001138 }
1139}
1140
1141/*
1142 * Check for changes in a buffer to affect stored syntax states. Uses the
1143 * b_mod_* fields.
1144 * Called from update_screen(), before screen is being updated, once for each
1145 * displayed buffer.
1146 */
1147 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001148syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001149{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001150 win_T *wp;
1151
1152 syn_stack_apply_changes_block(&buf->b_s, buf);
1153
1154 FOR_ALL_WINDOWS(wp)
1155 {
1156 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1157 syn_stack_apply_changes_block(wp->w_s, buf);
1158 }
1159}
1160
1161 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001162syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001163{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001164 synstate_T *p, *prev, *np;
1165 linenr_T n;
1166
Bram Moolenaar071d4272004-06-13 20:20:40 +00001167 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001168 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001169 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001170 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001171 {
1172 n = p->sst_lnum + buf->b_mod_xlines;
1173 if (n <= buf->b_mod_bot)
1174 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001175 // this state is inside the changed area, remove it
Bram Moolenaar071d4272004-06-13 20:20:40 +00001176 np = p->sst_next;
1177 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001178 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001179 else
1180 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001181 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001182 p = np;
1183 continue;
1184 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001185 // This state is below the changed area. Remember the line
1186 // that needs to be parsed before this entry can be made valid
1187 // again.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001188 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1189 {
1190 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1191 p->sst_change_lnum += buf->b_mod_xlines;
1192 else
1193 p->sst_change_lnum = buf->b_mod_top;
1194 }
1195 if (p->sst_change_lnum == 0
1196 || p->sst_change_lnum < buf->b_mod_bot)
1197 p->sst_change_lnum = buf->b_mod_bot;
1198
1199 p->sst_lnum = n;
1200 }
1201 prev = p;
1202 p = p->sst_next;
1203 }
1204}
1205
1206/*
1207 * Reduce the number of entries in the state stack for syn_buf.
1208 * Returns TRUE if at least one entry was freed.
1209 */
1210 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001211syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001212{
1213 synstate_T *p, *prev;
1214 disptick_T tick;
1215 int above;
1216 int dist;
1217 int retval = FALSE;
1218
Bram Moolenaar95892c22018-09-28 22:26:54 +02001219 if (syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001220 return retval;
1221
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001222 // Compute normal distance between non-displayed entries.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001223 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001224 dist = 999999;
1225 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001226 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001227
1228 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001229 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001230 * be removed. Set "above" when the "tick" for the oldest entry is above
1231 * "b_sst_lasttick" (the display tick wraps around).
1232 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001233 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001234 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001235 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001236 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1237 {
1238 if (prev->sst_lnum + dist > p->sst_lnum)
1239 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001240 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001241 {
1242 if (!above || p->sst_tick < tick)
1243 tick = p->sst_tick;
1244 above = TRUE;
1245 }
1246 else if (!above && p->sst_tick < tick)
1247 tick = p->sst_tick;
1248 }
1249 }
1250
1251 /*
1252 * Go through the list to make the entries for the oldest tick at an
1253 * interval of several lines.
1254 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001255 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001256 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1257 {
1258 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1259 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001260 // Move this entry from used list to free list
Bram Moolenaar071d4272004-06-13 20:20:40 +00001261 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001262 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001263 p = prev;
1264 retval = TRUE;
1265 }
1266 }
1267 return retval;
1268}
1269
1270/*
1271 * Free the allocated memory for a syn_state item.
1272 * Move the entry into the free list.
1273 */
1274 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001275syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001276{
1277 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001278 p->sst_next = block->b_sst_firstfree;
1279 block->b_sst_firstfree = p;
1280 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001281}
1282
1283/*
1284 * Find an entry in the list of state stacks at or before "lnum".
1285 * Returns NULL when there is no entry or the first entry is after "lnum".
1286 */
1287 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001288syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001289{
1290 synstate_T *p, *prev;
1291
1292 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001293 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001294 {
1295 if (p->sst_lnum == lnum)
1296 return p;
1297 if (p->sst_lnum > lnum)
1298 break;
1299 }
1300 return prev;
1301}
1302
1303/*
1304 * Try saving the current state in b_sst_array[].
1305 * The current state must be valid for the start of the current_lnum line!
1306 */
1307 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001308store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001309{
1310 int i;
1311 synstate_T *p;
1312 bufstate_T *bp;
1313 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001314 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001315
1316 /*
1317 * If the current state contains a start or end pattern that continues
1318 * from the previous line, we can't use it. Don't store it then.
1319 */
1320 for (i = current_state.ga_len - 1; i >= 0; --i)
1321 {
1322 cur_si = &CUR_STATE(i);
1323 if (cur_si->si_h_startpos.lnum >= current_lnum
1324 || cur_si->si_m_endpos.lnum >= current_lnum
1325 || cur_si->si_h_endpos.lnum >= current_lnum
1326 || (cur_si->si_end_idx
1327 && cur_si->si_eoe_pos.lnum >= current_lnum))
1328 break;
1329 }
1330 if (i >= 0)
1331 {
1332 if (sp != NULL)
1333 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001334 // find "sp" in the list and remove it
Bram Moolenaar860cae12010-06-05 23:22:07 +02001335 if (syn_block->b_sst_first == sp)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001336 // it's the first entry
Bram Moolenaar860cae12010-06-05 23:22:07 +02001337 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001338 else
1339 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001340 // find the entry just before this one to adjust sst_next
Bram Moolenaar00d253e2020-04-06 22:13:01 +02001341 FOR_ALL_SYNSTATES(syn_block, p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001342 if (p->sst_next == sp)
1343 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001344 if (p != NULL) // just in case
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001345 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001346 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001347 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001348 sp = NULL;
1349 }
1350 }
1351 else if (sp == NULL || sp->sst_lnum != current_lnum)
1352 {
1353 /*
1354 * Add a new entry
1355 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001356 // If no free items, cleanup the array first.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001357 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001358 {
1359 (void)syn_stack_cleanup();
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001360 // "sp" may have been moved to the freelist now
Bram Moolenaar071d4272004-06-13 20:20:40 +00001361 sp = syn_stack_find_entry(current_lnum);
1362 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001363 // Still no free items? Must be a strange problem...
Bram Moolenaar860cae12010-06-05 23:22:07 +02001364 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001365 sp = NULL;
1366 else
1367 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001368 // Take the first item from the free list and put it in the used
1369 // list, after *sp
Bram Moolenaar860cae12010-06-05 23:22:07 +02001370 p = syn_block->b_sst_firstfree;
1371 syn_block->b_sst_firstfree = p->sst_next;
1372 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001373 if (sp == NULL)
1374 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001375 // Insert in front of the list
Bram Moolenaar860cae12010-06-05 23:22:07 +02001376 p->sst_next = syn_block->b_sst_first;
1377 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001378 }
1379 else
1380 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001381 // insert in list after *sp
Bram Moolenaar071d4272004-06-13 20:20:40 +00001382 p->sst_next = sp->sst_next;
1383 sp->sst_next = p;
1384 }
1385 sp = p;
1386 sp->sst_stacksize = 0;
1387 sp->sst_lnum = current_lnum;
1388 }
1389 }
1390 if (sp != NULL)
1391 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001392 // When overwriting an existing state stack, clear it first
Bram Moolenaar071d4272004-06-13 20:20:40 +00001393 clear_syn_state(sp);
1394 sp->sst_stacksize = current_state.ga_len;
1395 if (current_state.ga_len > SST_FIX_STATES)
1396 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001397 // Need to clear it, might be something remaining from when the
1398 // length was less than SST_FIX_STATES.
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001399 ga_init2(&sp->sst_union.sst_ga, sizeof(bufstate_T), 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001400 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1401 sp->sst_stacksize = 0;
1402 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001403 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001404 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1405 }
1406 else
1407 bp = sp->sst_union.sst_stack;
1408 for (i = 0; i < sp->sst_stacksize; ++i)
1409 {
1410 bp[i].bs_idx = CUR_STATE(i).si_idx;
1411 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001412#ifdef FEAT_CONCEAL
1413 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1414 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1415#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001416 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1417 }
1418 sp->sst_next_flags = current_next_flags;
1419 sp->sst_next_list = current_next_list;
1420 sp->sst_tick = display_tick;
1421 sp->sst_change_lnum = 0;
1422 }
1423 current_state_stored = TRUE;
1424 return sp;
1425}
1426
1427/*
1428 * Copy a state stack from "from" in b_sst_array[] to current_state;
1429 */
1430 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001431load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001432{
1433 int i;
1434 bufstate_T *bp;
1435
1436 clear_current_state();
1437 validate_current_state();
1438 keepend_level = -1;
1439 if (from->sst_stacksize
Yegappan Lakshmananfadc02a2023-01-27 21:03:12 +00001440 && ga_grow(&current_state, from->sst_stacksize) == OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001441 {
1442 if (from->sst_stacksize > SST_FIX_STATES)
1443 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1444 else
1445 bp = from->sst_union.sst_stack;
1446 for (i = 0; i < from->sst_stacksize; ++i)
1447 {
1448 CUR_STATE(i).si_idx = bp[i].bs_idx;
1449 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001450#ifdef FEAT_CONCEAL
1451 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1452 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1453#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001454 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1455 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1456 keepend_level = i;
1457 CUR_STATE(i).si_ends = FALSE;
1458 CUR_STATE(i).si_m_lnum = 0;
1459 if (CUR_STATE(i).si_idx >= 0)
1460 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001461 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001462 else
1463 CUR_STATE(i).si_next_list = NULL;
1464 update_si_attr(i);
1465 }
1466 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001467 }
1468 current_next_list = from->sst_next_list;
1469 current_next_flags = from->sst_next_flags;
1470 current_lnum = from->sst_lnum;
1471}
1472
1473/*
1474 * Compare saved state stack "*sp" with the current state.
1475 * Return TRUE when they are equal.
1476 */
1477 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001478syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001479{
1480 int i, j;
1481 bufstate_T *bp;
1482 reg_extmatch_T *six, *bsx;
1483
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001484 // First a quick check if the stacks have the same size end nextlist.
zeertzjq101d57b2022-07-31 18:34:32 +01001485 if (sp->sst_stacksize != current_state.ga_len
1486 || sp->sst_next_list != current_next_list)
1487 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001488
zeertzjq101d57b2022-07-31 18:34:32 +01001489 // Need to compare all states on both stacks.
1490 if (sp->sst_stacksize > SST_FIX_STATES)
1491 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1492 else
1493 bp = sp->sst_union.sst_stack;
1494
1495 for (i = current_state.ga_len; --i >= 0; )
1496 {
1497 // If the item has another index the state is different.
1498 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1499 break;
1500 if (bp[i].bs_extmatch == CUR_STATE(i).si_extmatch)
1501 continue;
1502 // When the extmatch pointers are different, the strings in them can
1503 // still be the same. Check if the extmatch references are equal.
1504 bsx = bp[i].bs_extmatch;
1505 six = CUR_STATE(i).si_extmatch;
1506 // If one of the extmatch pointers is NULL the states are different.
1507 if (bsx == NULL || six == NULL)
1508 break;
1509 for (j = 0; j < NSUBEXP; ++j)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001510 {
zeertzjq101d57b2022-07-31 18:34:32 +01001511 // Check each referenced match string. They must all be equal.
1512 if (bsx->matches[j] != six->matches[j])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001513 {
zeertzjq101d57b2022-07-31 18:34:32 +01001514 // If the pointer is different it can still be the same text.
1515 // Compare the strings, ignore case when the start item has the
1516 // sp_ic flag set.
1517 if (bsx->matches[j] == NULL || six->matches[j] == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001518 break;
zeertzjq101d57b2022-07-31 18:34:32 +01001519 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
1520 ? MB_STRICMP(bsx->matches[j], six->matches[j]) != 0
1521 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001522 break;
1523 }
1524 }
zeertzjq101d57b2022-07-31 18:34:32 +01001525 if (j != NSUBEXP)
1526 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001527 }
zeertzjq101d57b2022-07-31 18:34:32 +01001528 return i < 0 ? TRUE : FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001529}
1530
1531/*
1532 * We stop parsing syntax above line "lnum". If the stored state at or below
1533 * this line depended on a change before it, it now depends on the line below
1534 * the last parsed line.
1535 * The window looks like this:
1536 * line which changed
1537 * displayed line
1538 * displayed line
1539 * lnum -> line below window
1540 */
1541 void
Bram Moolenaar0abd6cf2022-10-14 17:04:09 +01001542syntax_end_parsing(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001543{
1544 synstate_T *sp;
1545
Bram Moolenaar0abd6cf2022-10-14 17:04:09 +01001546 if (syn_block != wp->w_s)
1547 return; // not the right window
Bram Moolenaar071d4272004-06-13 20:20:40 +00001548 sp = syn_stack_find_entry(lnum);
1549 if (sp != NULL && sp->sst_lnum < lnum)
1550 sp = sp->sst_next;
1551
1552 if (sp != NULL && sp->sst_change_lnum != 0)
1553 sp->sst_change_lnum = lnum;
1554}
1555
1556/*
1557 * End of handling of the state stack.
1558 ****************************************/
1559
1560 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001561invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001562{
1563 clear_current_state();
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001564 current_state.ga_itemsize = 0; // mark current_state invalid
Bram Moolenaar071d4272004-06-13 20:20:40 +00001565 current_next_list = NULL;
1566 keepend_level = -1;
1567}
1568
1569 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001570validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001571{
1572 current_state.ga_itemsize = sizeof(stateitem_T);
1573 current_state.ga_growsize = 3;
1574}
1575
1576/*
1577 * Return TRUE if the syntax at start of lnum changed since last time.
1578 * This will only be called just after get_syntax_attr() for the previous
1579 * line, to check if the next line needs to be redrawn too.
1580 */
1581 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001582syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001583{
1584 int retval = TRUE;
1585 synstate_T *sp;
1586
Bram Moolenaar071d4272004-06-13 20:20:40 +00001587 /*
1588 * Check the state stack when:
1589 * - lnum is just below the previously syntaxed line.
1590 * - lnum is not before the lines with saved states.
1591 * - lnum is not past the lines with saved states.
1592 * - lnum is at or before the last changed line.
1593 */
1594 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1595 {
1596 sp = syn_stack_find_entry(lnum);
1597 if (sp != NULL && sp->sst_lnum == lnum)
1598 {
1599 /*
1600 * finish the previous line (needed when not all of the line was
1601 * drawn)
1602 */
1603 (void)syn_finish_line(FALSE);
1604
1605 /*
1606 * Compare the current state with the previously saved state of
1607 * the line.
1608 */
1609 if (syn_stack_equal(sp))
1610 retval = FALSE;
1611
1612 /*
1613 * Store the current state in b_sst_array[] for later use.
1614 */
1615 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001616 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001617 }
1618 }
1619
Bram Moolenaar071d4272004-06-13 20:20:40 +00001620 return retval;
1621}
1622
1623/*
1624 * Finish the current line.
1625 * This doesn't return any attributes, it only gets the state at the end of
1626 * the line. It can start anywhere in the line, as long as the current state
1627 * is valid.
1628 */
1629 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001630syn_finish_line(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001631 int syncing) // called for syncing
Bram Moolenaar071d4272004-06-13 20:20:40 +00001632{
1633 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001634 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001635
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001636 while (!current_finished)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001637 {
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001638 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1639 /*
1640 * When syncing, and found some item, need to check the item.
1641 */
1642 if (syncing && current_state.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001643 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001644 /*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001645 * Check for match with sync item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001646 */
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001647 cur_si = &CUR_STATE(current_state.ga_len - 1);
1648 if (cur_si->si_idx >= 0
1649 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1650 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1651 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001652
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001653 // syn_current_attr() will have skipped the check for an item
1654 // that ends here, need to do that now. Be careful not to go
1655 // past the NUL.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001656 prev_current_col = current_col;
1657 if (syn_getcurline()[current_col] != NUL)
1658 ++current_col;
1659 check_state_ends();
1660 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001661 }
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001662 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001663 }
1664 return FALSE;
1665}
1666
1667/*
1668 * Return highlight attributes for next character.
1669 * Must first call syntax_start() once for the line.
1670 * "col" is normally 0 for the first use in a line, and increments by one each
1671 * time. It's allowed to skip characters and to stop before the end of the
1672 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001673 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1674 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001675 */
1676 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001677get_syntax_attr(
1678 colnr_T col,
1679 int *can_spell,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001680 int keep_state) // keep state of char at "col"
Bram Moolenaar071d4272004-06-13 20:20:40 +00001681{
1682 int attr = 0;
1683
Bram Moolenaar349955a2007-08-14 21:07:36 +00001684 if (can_spell != NULL)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001685 // Default: Only do spelling when there is no @Spell cluster or when
1686 // ":syn spell toplevel" was used.
Bram Moolenaar860cae12010-06-05 23:22:07 +02001687 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1688 ? (syn_block->b_spell_cluster_id == 0)
1689 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001690
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001691 // check for out of memory situation
Bram Moolenaar860cae12010-06-05 23:22:07 +02001692 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001693 return 0;
1694
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001695 // After 'synmaxcol' the attribute is always zero.
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001696 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001697 {
1698 clear_current_state();
1699#ifdef FEAT_EVAL
1700 current_id = 0;
1701 current_trans_id = 0;
1702#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001703#ifdef FEAT_CONCEAL
1704 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001705 current_seqnr = 0;
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001706#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001707 return 0;
1708 }
1709
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001710 // Make sure current_state is valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00001711 if (INVALID_STATE(&current_state))
1712 validate_current_state();
1713
1714 /*
1715 * Skip from the current column to "col", get the attributes for "col".
1716 */
1717 while (current_col <= col)
1718 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001719 attr = syn_current_attr(FALSE, TRUE, can_spell,
1720 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001721 ++current_col;
1722 }
1723
Bram Moolenaar071d4272004-06-13 20:20:40 +00001724 return attr;
1725}
1726
1727/*
1728 * Get syntax attributes for current_lnum, current_col.
1729 */
1730 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001731syn_current_attr(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001732 int syncing, // When 1: called for syncing
1733 int displaying, // result will be displayed
1734 int *can_spell, // return: do spell checking
1735 int keep_state) // keep syntax stack afterwards
Bram Moolenaar071d4272004-06-13 20:20:40 +00001736{
1737 int syn_id;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001738 lpos_T endpos; // was: char_u *endp;
1739 lpos_T hl_startpos; // was: int hl_startcol;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001740 lpos_T hl_endpos;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001741 lpos_T eos_pos; // end-of-start match (start region)
1742 lpos_T eoe_pos; // end-of-end pattern
1743 int end_idx; // group ID for end pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00001744 int idx;
1745 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001746 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001747 int startcol;
1748 int endcol;
1749 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001750 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001751 short *next_list;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001752 int found_match; // found usable match
1753 static int try_next_column = FALSE; // must try in next col
Bram Moolenaar071d4272004-06-13 20:20:40 +00001754 int do_keywords;
1755 regmmatch_T regmatch;
1756 lpos_T pos;
1757 int lc_col;
1758 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001759 char_u buf_chartab[32]; // chartab array for syn iskyeyword
1760 char_u *line; // current line. NOTE: becomes invalid after
1761 // looking for a pattern match!
Bram Moolenaar071d4272004-06-13 20:20:40 +00001762
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001763 // variables for zero-width matches that have a "nextgroup" argument
Bram Moolenaar071d4272004-06-13 20:20:40 +00001764 int keep_next_list;
1765 int zero_width_next_list = FALSE;
1766 garray_T zero_width_next_ga;
1767
1768 /*
1769 * No character, no attributes! Past end of line?
1770 * Do try matching with an empty line (could be the start of a region).
1771 */
1772 line = syn_getcurline();
1773 if (line[current_col] == NUL && current_col != 0)
1774 {
1775 /*
1776 * If we found a match after the last column, use it.
1777 */
1778 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1779 && next_match_col != MAXCOL)
1780 (void)push_next_match(NULL);
1781
1782 current_finished = TRUE;
1783 current_state_stored = FALSE;
1784 return 0;
1785 }
1786
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001787 // if the current or next character is NUL, we will finish the line now
Bram Moolenaar071d4272004-06-13 20:20:40 +00001788 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1789 {
1790 current_finished = TRUE;
1791 current_state_stored = FALSE;
1792 }
1793
1794 /*
1795 * When in the previous column there was a match but it could not be used
1796 * (empty match or already matched in this column) need to try again in
1797 * the next column.
1798 */
1799 if (try_next_column)
1800 {
1801 next_match_idx = -1;
1802 try_next_column = FALSE;
1803 }
1804
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001805 // Only check for keywords when not syncing and there are some.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001806 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001807 && (syn_block->b_keywtab.ht_used > 0
1808 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001809
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001810 // Init the list of zero-width matches with a nextlist. This is used to
1811 // avoid matching the same item in the same position twice.
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001812 ga_init2(&zero_width_next_ga, sizeof(int), 10);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001813
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001814 // use syntax iskeyword option
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001815 save_chartab(buf_chartab);
1816
Bram Moolenaar071d4272004-06-13 20:20:40 +00001817 /*
1818 * Repeat matching keywords and patterns, to find contained items at the
1819 * same column. This stops when there are no extra matches at the current
1820 * column.
1821 */
1822 do
1823 {
1824 found_match = FALSE;
1825 keep_next_list = FALSE;
1826 syn_id = 0;
1827
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001828
Bram Moolenaar071d4272004-06-13 20:20:40 +00001829 /*
1830 * 1. Check for a current state.
1831 * Only when there is no current state, or if the current state may
1832 * contain other things, we need to check for keywords and patterns.
1833 * Always need to check for contained items if some item has the
1834 * "containedin" argument (takes extra time!).
1835 */
1836 if (current_state.ga_len)
1837 cur_si = &CUR_STATE(current_state.ga_len - 1);
1838 else
1839 cur_si = NULL;
1840
Bram Moolenaar860cae12010-06-05 23:22:07 +02001841 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001842 || cur_si->si_cont_list != NULL)
1843 {
1844 /*
1845 * 2. Check for keywords, if on a keyword char after a non-keyword
1846 * char. Don't do this when syncing.
1847 */
1848 if (do_keywords)
1849 {
1850 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001851 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001852 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001853 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00001854 - (has_mbyte
1855 ? (*mb_head_off)(line, line + current_col - 1)
Bram Moolenaar264b74f2019-01-24 17:18:42 +01001856 : 0) , syn_buf)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001857 {
1858 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001859 &endcol, &flags, &next_list, cur_si,
1860 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001861 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001862 {
1863 if (push_current_state(KEYWORD_IDX) == OK)
1864 {
1865 cur_si = &CUR_STATE(current_state.ga_len - 1);
1866 cur_si->si_m_startcol = current_col;
1867 cur_si->si_h_startpos.lnum = current_lnum;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001868 cur_si->si_h_startpos.col = 0; // starts right away
Bram Moolenaar071d4272004-06-13 20:20:40 +00001869 cur_si->si_m_endpos.lnum = current_lnum;
1870 cur_si->si_m_endpos.col = endcol;
1871 cur_si->si_h_endpos.lnum = current_lnum;
1872 cur_si->si_h_endpos.col = endcol;
1873 cur_si->si_ends = TRUE;
1874 cur_si->si_end_idx = 0;
1875 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001876#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02001877 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001878 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001879 if (current_state.ga_len > 1)
1880 cur_si->si_flags |=
1881 CUR_STATE(current_state.ga_len - 2).si_flags
1882 & HL_CONCEAL;
1883#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001884 cur_si->si_id = syn_id;
1885 cur_si->si_trans_id = syn_id;
1886 if (flags & HL_TRANSP)
1887 {
1888 if (current_state.ga_len < 2)
1889 {
1890 cur_si->si_attr = 0;
1891 cur_si->si_trans_id = 0;
1892 }
1893 else
1894 {
1895 cur_si->si_attr = CUR_STATE(
1896 current_state.ga_len - 2).si_attr;
1897 cur_si->si_trans_id = CUR_STATE(
1898 current_state.ga_len - 2).si_trans_id;
1899 }
1900 }
1901 else
1902 cur_si->si_attr = syn_id2attr(syn_id);
1903 cur_si->si_cont_list = NULL;
1904 cur_si->si_next_list = next_list;
1905 check_keepend();
1906 }
1907 else
1908 vim_free(next_list);
1909 }
1910 }
1911 }
1912
1913 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001914 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001915 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001916 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001917 {
1918 /*
1919 * If we didn't check for a match yet, or we are past it, check
1920 * for any match with a pattern.
1921 */
1922 if (next_match_idx < 0 || next_match_col < (int)current_col)
1923 {
1924 /*
1925 * Check all relevant patterns for a match at this
1926 * position. This is complicated, because matching with a
1927 * pattern takes quite a bit of time, thus we want to
1928 * avoid doing it when it's not needed.
1929 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001930 next_match_idx = 0; // no match in this line yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00001931 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001932 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001933 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001934 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001935 if ( spp->sp_syncing == syncing
1936 && (displaying || !(spp->sp_flags & HL_DISPLAY))
1937 && (spp->sp_type == SPTYPE_MATCH
1938 || spp->sp_type == SPTYPE_START)
1939 && (current_next_list != NULL
1940 ? in_id_list(NULL, current_next_list,
1941 &spp->sp_syn, 0)
1942 : (cur_si == NULL
1943 ? !(spp->sp_flags & HL_CONTAINED)
1944 : in_id_list(cur_si,
1945 cur_si->si_cont_list, &spp->sp_syn,
Theodore Duboisf50d5362025-02-05 23:59:25 +01001946 spp->sp_flags))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001947 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001948 int r;
1949
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001950 // If we already tried matching in this line, and
1951 // there isn't a match before next_match_col, skip
1952 // this item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001953 if (spp->sp_line_id == current_line_id
1954 && spp->sp_startcol >= next_match_col)
1955 continue;
1956 spp->sp_line_id = current_line_id;
1957
1958 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
1959 if (lc_col < 0)
1960 lc_col = 0;
1961
1962 regmatch.rmm_ic = spp->sp_ic;
1963 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001964 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001965 current_lnum,
1966 (colnr_T)lc_col,
Bram Moolenaard23a8232018-02-10 18:45:26 +01001967 IF_SYN_TIME(&spp->sp_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001968 spp->sp_prog = regmatch.regprog;
1969 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001970 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001971 // no match in this line, try another one
Bram Moolenaar071d4272004-06-13 20:20:40 +00001972 spp->sp_startcol = MAXCOL;
1973 continue;
1974 }
1975
1976 /*
1977 * Compute the first column of the match.
1978 */
1979 syn_add_start_off(&pos, &regmatch,
1980 spp, SPO_MS_OFF, -1);
1981 if (pos.lnum > current_lnum)
1982 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001983 // must have used end of match in a next line,
1984 // we can't handle that
Bram Moolenaar071d4272004-06-13 20:20:40 +00001985 spp->sp_startcol = MAXCOL;
1986 continue;
1987 }
1988 startcol = pos.col;
1989
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001990 // remember the next column where this pattern
1991 // matches in the current line
Bram Moolenaar071d4272004-06-13 20:20:40 +00001992 spp->sp_startcol = startcol;
1993
1994 /*
1995 * If a previously found match starts at a lower
1996 * column number, don't use this one.
1997 */
1998 if (startcol >= next_match_col)
1999 continue;
2000
2001 /*
2002 * If we matched this pattern at this position
2003 * before, skip it. Must retry in the next
2004 * column, because it may match from there.
2005 */
2006 if (did_match_already(idx, &zero_width_next_ga))
2007 {
2008 try_next_column = TRUE;
2009 continue;
2010 }
2011
2012 endpos.lnum = regmatch.endpos[0].lnum;
2013 endpos.col = regmatch.endpos[0].col;
2014
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002015 // Compute the highlight start.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002016 syn_add_start_off(&hl_startpos, &regmatch,
2017 spp, SPO_HS_OFF, -1);
2018
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002019 // Compute the region start.
2020 // Default is to use the end of the match.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002021 syn_add_end_off(&eos_pos, &regmatch,
2022 spp, SPO_RS_OFF, 0);
2023
2024 /*
2025 * Grab the external submatches before they get
2026 * overwritten. Reference count doesn't change.
2027 */
2028 unref_extmatch(cur_extmatch);
2029 cur_extmatch = re_extmatch_out;
2030 re_extmatch_out = NULL;
2031
2032 flags = 0;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002033 eoe_pos.lnum = 0; // avoid warning
Bram Moolenaar071d4272004-06-13 20:20:40 +00002034 eoe_pos.col = 0;
2035 end_idx = 0;
2036 hl_endpos.lnum = 0;
2037
2038 /*
2039 * For a "oneline" the end must be found in the
2040 * same line too. Search for it after the end of
2041 * the match with the start pattern. Set the
2042 * resulting end positions at the same time.
2043 */
2044 if (spp->sp_type == SPTYPE_START
2045 && (spp->sp_flags & HL_ONELINE))
2046 {
2047 lpos_T startpos;
2048
2049 startpos = endpos;
2050 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2051 &flags, &eoe_pos, &end_idx, cur_extmatch);
2052 if (endpos.lnum == 0)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002053 continue; // not found
Bram Moolenaar071d4272004-06-13 20:20:40 +00002054 }
2055
2056 /*
2057 * For a "match" the size must be > 0 after the
2058 * end offset needs has been added. Except when
2059 * syncing.
2060 */
2061 else if (spp->sp_type == SPTYPE_MATCH)
2062 {
2063 syn_add_end_off(&hl_endpos, &regmatch, spp,
2064 SPO_HE_OFF, 0);
2065 syn_add_end_off(&endpos, &regmatch, spp,
2066 SPO_ME_OFF, 0);
2067 if (endpos.lnum == current_lnum
2068 && (int)endpos.col + syncing < startcol)
2069 {
2070 /*
2071 * If an empty string is matched, may need
2072 * to try matching again at next column.
2073 */
2074 if (regmatch.startpos[0].col
2075 == regmatch.endpos[0].col)
2076 try_next_column = TRUE;
2077 continue;
2078 }
2079 }
2080
2081 /*
2082 * keep the best match so far in next_match_*
2083 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002084 // Highlighting must start after startpos and end
2085 // before endpos.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002086 if (hl_startpos.lnum == current_lnum
2087 && (int)hl_startpos.col < startcol)
2088 hl_startpos.col = startcol;
2089 limit_pos_zero(&hl_endpos, &endpos);
2090
2091 next_match_idx = idx;
2092 next_match_col = startcol;
2093 next_match_m_endpos = endpos;
2094 next_match_h_endpos = hl_endpos;
2095 next_match_h_startpos = hl_startpos;
2096 next_match_flags = flags;
2097 next_match_eos_pos = eos_pos;
2098 next_match_eoe_pos = eoe_pos;
2099 next_match_end_idx = end_idx;
2100 unref_extmatch(next_match_extmatch);
2101 next_match_extmatch = cur_extmatch;
2102 cur_extmatch = NULL;
2103 }
2104 }
2105 }
2106
2107 /*
2108 * If we found a match at the current column, use it.
2109 */
2110 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2111 {
2112 synpat_T *lspp;
2113
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002114 // When a zero-width item matched which has a nextgroup,
2115 // don't push the item but set nextgroup.
Bram Moolenaar860cae12010-06-05 23:22:07 +02002116 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002117 if (next_match_m_endpos.lnum == current_lnum
2118 && next_match_m_endpos.col == current_col
2119 && lspp->sp_next_list != NULL)
2120 {
2121 current_next_list = lspp->sp_next_list;
2122 current_next_flags = lspp->sp_flags;
2123 keep_next_list = TRUE;
2124 zero_width_next_list = TRUE;
2125
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002126 // Add the index to a list, so that we can check
2127 // later that we don't match it again (and cause an
2128 // endless loop).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002129 if (ga_grow(&zero_width_next_ga, 1) == OK)
2130 {
2131 ((int *)(zero_width_next_ga.ga_data))
2132 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002133 }
2134 next_match_idx = -1;
2135 }
2136 else
2137 cur_si = push_next_match(cur_si);
2138 found_match = TRUE;
2139 }
2140 }
2141 }
2142
2143 /*
2144 * Handle searching for nextgroup match.
2145 */
2146 if (current_next_list != NULL && !keep_next_list)
2147 {
2148 /*
2149 * If a nextgroup was not found, continue looking for one if:
2150 * - this is an empty line and the "skipempty" option was given
2151 * - we are on white space and the "skipwhite" option was given
2152 */
2153 if (!found_match)
2154 {
2155 line = syn_getcurline();
2156 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002157 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002158 || ((current_next_flags & HL_SKIPEMPTY)
2159 && *line == NUL))
2160 break;
2161 }
2162
2163 /*
2164 * If a nextgroup was found: Use it, and continue looking for
2165 * contained matches.
2166 * If a nextgroup was not found: Continue looking for a normal
2167 * match.
2168 * When did set current_next_list for a zero-width item and no
2169 * match was found don't loop (would get stuck).
2170 */
2171 current_next_list = NULL;
2172 next_match_idx = -1;
2173 if (!zero_width_next_list)
2174 found_match = TRUE;
2175 }
2176
2177 } while (found_match);
2178
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002179 restore_chartab(buf_chartab);
2180
Bram Moolenaar071d4272004-06-13 20:20:40 +00002181 /*
2182 * Use attributes from the current state, if within its highlighting.
2183 * If not, use attributes from the current-but-one state, etc.
2184 */
2185 current_attr = 0;
2186#ifdef FEAT_EVAL
2187 current_id = 0;
2188 current_trans_id = 0;
2189#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002190#ifdef FEAT_CONCEAL
2191 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02002192 current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002193#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002194 if (cur_si != NULL)
2195 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002196#ifndef FEAT_EVAL
2197 int current_trans_id = 0;
2198#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002199 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2200 {
2201 sip = &CUR_STATE(idx);
2202 if ((current_lnum > sip->si_h_startpos.lnum
2203 || (current_lnum == sip->si_h_startpos.lnum
2204 && current_col >= sip->si_h_startpos.col))
2205 && (sip->si_h_endpos.lnum == 0
2206 || current_lnum < sip->si_h_endpos.lnum
2207 || (current_lnum == sip->si_h_endpos.lnum
2208 && current_col < sip->si_h_endpos.col)))
2209 {
2210 current_attr = sip->si_attr;
2211#ifdef FEAT_EVAL
2212 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002213#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002214 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002215#ifdef FEAT_CONCEAL
2216 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002217 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002218 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002219#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002220 break;
2221 }
2222 }
2223
Bram Moolenaar217ad922005-03-20 22:37:15 +00002224 if (can_spell != NULL)
2225 {
2226 struct sp_syn sps;
2227
2228 /*
2229 * set "can_spell" to TRUE if spell checking is supposed to be
2230 * done in the current item.
2231 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002232 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002233 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002234 // There is no @Spell cluster: Do spelling for items without
2235 // @NoSpell cluster.
Bram Moolenaar860cae12010-06-05 23:22:07 +02002236 if (syn_block->b_nospell_cluster_id == 0
2237 || current_trans_id == 0)
2238 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002239 else
2240 {
2241 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002242 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002243 sps.cont_in_list = NULL;
2244 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2245 }
2246 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002247 else
2248 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002249 // The @Spell cluster is defined: Do spelling in items with
2250 // the @Spell cluster. But not when @NoSpell is also there.
2251 // At the toplevel only spell check when ":syn spell toplevel"
2252 // was used.
Bram Moolenaar3638c682005-06-08 22:05:14 +00002253 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002254 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002255 else
2256 {
2257 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002258 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002259 sps.cont_in_list = NULL;
2260 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2261
Bram Moolenaar860cae12010-06-05 23:22:07 +02002262 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002263 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002264 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002265 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2266 *can_spell = FALSE;
2267 }
2268 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002269 }
2270 }
2271
2272
Bram Moolenaar071d4272004-06-13 20:20:40 +00002273 /*
2274 * Check for end of current state (and the states before it) at the
2275 * next column. Don't do this for syncing, because we would miss a
2276 * single character match.
2277 * First check if the current state ends at the current column. It
2278 * may be for an empty match and a containing item might end in the
2279 * current column.
2280 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002281 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002282 {
2283 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002284 if (current_state.ga_len > 0
2285 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002286 {
2287 ++current_col;
2288 check_state_ends();
2289 --current_col;
2290 }
2291 }
2292 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002293 else if (can_spell != NULL)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002294 // Default: Only do spelling when there is no @Spell cluster or when
2295 // ":syn spell toplevel" was used.
Bram Moolenaar860cae12010-06-05 23:22:07 +02002296 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2297 ? (syn_block->b_spell_cluster_id == 0)
2298 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002299
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002300 // nextgroup ends at end of line, unless "skipnl" or "skipempty" present
Bram Moolenaar071d4272004-06-13 20:20:40 +00002301 if (current_next_list != NULL
Bram Moolenaar069dafc2018-03-03 20:02:19 +01002302 && (line = syn_getcurline())[current_col] != NUL
2303 && line[current_col + 1] == NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002304 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2305 current_next_list = NULL;
2306
2307 if (zero_width_next_ga.ga_len > 0)
2308 ga_clear(&zero_width_next_ga);
2309
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002310 // No longer need external matches. But keep next_match_extmatch.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002311 unref_extmatch(re_extmatch_out);
2312 re_extmatch_out = NULL;
2313 unref_extmatch(cur_extmatch);
2314
2315 return current_attr;
2316}
2317
2318
2319/*
2320 * Check if we already matched pattern "idx" at the current column.
2321 */
2322 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002323did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002324{
2325 int i;
2326
2327 for (i = current_state.ga_len; --i >= 0; )
2328 if (CUR_STATE(i).si_m_startcol == (int)current_col
2329 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2330 && CUR_STATE(i).si_idx == idx)
2331 return TRUE;
2332
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002333 // Zero-width matches with a nextgroup argument are not put on the syntax
2334 // stack, and can only be matched once anyway.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002335 for (i = gap->ga_len; --i >= 0; )
2336 if (((int *)(gap->ga_data))[i] == idx)
2337 return TRUE;
2338
2339 return FALSE;
2340}
2341
2342/*
2343 * Push the next match onto the stack.
2344 */
2345 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002346push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002347{
2348 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002349#ifdef FEAT_CONCEAL
2350 int save_flags;
2351#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002352
Bram Moolenaar860cae12010-06-05 23:22:07 +02002353 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002354
2355 /*
2356 * Push the item in current_state stack;
2357 */
2358 if (push_current_state(next_match_idx) == OK)
2359 {
2360 /*
2361 * If it's a start-skip-end type that crosses lines, figure out how
2362 * much it continues in this line. Otherwise just fill in the length.
2363 */
2364 cur_si = &CUR_STATE(current_state.ga_len - 1);
2365 cur_si->si_h_startpos = next_match_h_startpos;
2366 cur_si->si_m_startcol = current_col;
2367 cur_si->si_m_lnum = current_lnum;
2368 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002369#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002370 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002371 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002372 if (current_state.ga_len > 1)
2373 cur_si->si_flags |=
2374 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2375#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002376 cur_si->si_next_list = spp->sp_next_list;
2377 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2378 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2379 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002380 // Try to find the end pattern in the current line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002381 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2382 check_keepend();
2383 }
2384 else
2385 {
2386 cur_si->si_m_endpos = next_match_m_endpos;
2387 cur_si->si_h_endpos = next_match_h_endpos;
2388 cur_si->si_ends = TRUE;
2389 cur_si->si_flags |= next_match_flags;
2390 cur_si->si_eoe_pos = next_match_eoe_pos;
2391 cur_si->si_end_idx = next_match_end_idx;
2392 }
2393 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2394 keepend_level = current_state.ga_len - 1;
2395 check_keepend();
2396 update_si_attr(current_state.ga_len - 1);
2397
Bram Moolenaar860cae12010-06-05 23:22:07 +02002398#ifdef FEAT_CONCEAL
2399 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2400#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002401 /*
2402 * If the start pattern has another highlight group, push another item
2403 * on the stack for the start pattern.
2404 */
2405 if ( spp->sp_type == SPTYPE_START
2406 && spp->sp_syn_match_id != 0
2407 && push_current_state(next_match_idx) == OK)
2408 {
2409 cur_si = &CUR_STATE(current_state.ga_len - 1);
2410 cur_si->si_h_startpos = next_match_h_startpos;
2411 cur_si->si_m_startcol = current_col;
2412 cur_si->si_m_lnum = current_lnum;
2413 cur_si->si_m_endpos = next_match_eos_pos;
2414 cur_si->si_h_endpos = next_match_eos_pos;
2415 cur_si->si_ends = TRUE;
2416 cur_si->si_end_idx = 0;
2417 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002418#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002419 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002420 cur_si->si_flags |= save_flags;
2421 if (cur_si->si_flags & HL_CONCEALENDS)
2422 cur_si->si_flags |= HL_CONCEAL;
2423#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002424 cur_si->si_next_list = NULL;
2425 check_keepend();
2426 update_si_attr(current_state.ga_len - 1);
2427 }
2428 }
2429
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002430 next_match_idx = -1; // try other match next time
Bram Moolenaar071d4272004-06-13 20:20:40 +00002431
2432 return cur_si;
2433}
2434
2435/*
2436 * Check for end of current state (and the states before it).
2437 */
2438 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002439check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002440{
2441 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002442 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002443
2444 cur_si = &CUR_STATE(current_state.ga_len - 1);
2445 for (;;)
2446 {
2447 if (cur_si->si_ends
2448 && (cur_si->si_m_endpos.lnum < current_lnum
2449 || (cur_si->si_m_endpos.lnum == current_lnum
2450 && cur_si->si_m_endpos.col <= current_col)))
2451 {
2452 /*
2453 * If there is an end pattern group ID, highlight the end pattern
2454 * now. No need to pop the current item from the stack.
2455 * Only do this if the end pattern continues beyond the current
2456 * position.
2457 */
2458 if (cur_si->si_end_idx
2459 && (cur_si->si_eoe_pos.lnum > current_lnum
2460 || (cur_si->si_eoe_pos.lnum == current_lnum
2461 && cur_si->si_eoe_pos.col > current_col)))
2462 {
2463 cur_si->si_idx = cur_si->si_end_idx;
2464 cur_si->si_end_idx = 0;
2465 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2466 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2467 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002468#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002469 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002470 if (cur_si->si_flags & HL_CONCEALENDS)
2471 cur_si->si_flags |= HL_CONCEAL;
2472#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002473 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002474
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002475 // nextgroup= should not match in the end pattern
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002476 current_next_list = NULL;
2477
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002478 // what matches next may be different now, clear it
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002479 next_match_idx = 0;
2480 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002481 break;
2482 }
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002483
2484 // handle next_list, unless at end of line and no "skipnl" or
2485 // "skipempty"
2486 current_next_list = cur_si->si_next_list;
2487 current_next_flags = cur_si->si_flags;
2488 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2489 && syn_getcurline()[current_col] == NUL)
2490 current_next_list = NULL;
2491
2492 // When the ended item has "extend", another item with
2493 // "keepend" now needs to check for its end.
2494 had_extend = (cur_si->si_flags & HL_EXTEND);
2495
2496 pop_current_state();
2497
2498 if (current_state.ga_len == 0)
2499 break;
2500
2501 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002502 {
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002503 syn_update_ends(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002504 if (current_state.ga_len == 0)
2505 break;
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002506 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002507
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002508 cur_si = &CUR_STATE(current_state.ga_len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002509
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01002510 /*
2511 * Only for a region the search for the end continues after
2512 * the end of the contained item. If the contained match
2513 * included the end-of-line, break here, the region continues.
2514 * Don't do this when:
2515 * - "keepend" is used for the contained item
2516 * - not at the end of the line (could be end="x$"me=e-1).
2517 * - "excludenl" is used (HL_HAS_EOL won't be set)
2518 */
2519 if (cur_si->si_idx >= 0
2520 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
2521 == SPTYPE_START
2522 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2523 {
2524 update_si_end(cur_si, (int)current_col, TRUE);
2525 check_keepend();
2526 if ((current_next_flags & HL_HAS_EOL)
2527 && keepend_level < 0
2528 && syn_getcurline()[current_col] == NUL)
2529 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002530 }
2531 }
2532 else
2533 break;
2534 }
2535}
2536
2537/*
2538 * Update an entry in the current_state stack for a match or region. This
2539 * fills in si_attr, si_next_list and si_cont_list.
2540 */
2541 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002542update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002543{
2544 stateitem_T *sip = &CUR_STATE(idx);
2545 synpat_T *spp;
2546
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002547 // This should not happen...
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002548 if (sip->si_idx < 0)
2549 return;
2550
Bram Moolenaar860cae12010-06-05 23:22:07 +02002551 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002552 if (sip->si_flags & HL_MATCH)
2553 sip->si_id = spp->sp_syn_match_id;
2554 else
2555 sip->si_id = spp->sp_syn.id;
2556 sip->si_attr = syn_id2attr(sip->si_id);
2557 sip->si_trans_id = sip->si_id;
2558 if (sip->si_flags & HL_MATCH)
2559 sip->si_cont_list = NULL;
2560 else
2561 sip->si_cont_list = spp->sp_cont_list;
2562
2563 /*
2564 * For transparent items, take attr from outer item.
2565 * Also take cont_list, if there is none.
2566 * Don't do this for the matchgroup of a start or end pattern.
2567 */
2568 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2569 {
2570 if (idx == 0)
2571 {
2572 sip->si_attr = 0;
2573 sip->si_trans_id = 0;
2574 if (sip->si_cont_list == NULL)
2575 sip->si_cont_list = ID_LIST_ALL;
2576 }
2577 else
2578 {
2579 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2580 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
2581 if (sip->si_cont_list == NULL)
2582 {
2583 sip->si_flags |= HL_TRANS_CONT;
2584 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2585 }
2586 }
2587 }
2588}
2589
2590/*
2591 * Check the current stack for patterns with "keepend" flag.
2592 * Propagate the match-end to contained items, until a "skipend" item is found.
2593 */
2594 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002595check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002596{
2597 int i;
2598 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002599 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002600 stateitem_T *sip;
2601
2602 /*
2603 * This check can consume a lot of time; only do it from the level where
2604 * there really is a keepend.
2605 */
2606 if (keepend_level < 0)
2607 return;
2608
2609 /*
2610 * Find the last index of an "extend" item. "keepend" items before that
2611 * won't do anything. If there is no "extend" item "i" will be
2612 * "keepend_level" and all "keepend" items will work normally.
2613 */
2614 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2615 if (CUR_STATE(i).si_flags & HL_EXTEND)
2616 break;
2617
2618 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002619 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002620 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002621 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002622 for ( ; i < current_state.ga_len; ++i)
2623 {
2624 sip = &CUR_STATE(i);
2625 if (maxpos.lnum != 0)
2626 {
2627 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002628 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002629 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2630 sip->si_ends = TRUE;
2631 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002632 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2633 {
2634 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002635 || maxpos.lnum > sip->si_m_endpos.lnum
2636 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002637 && maxpos.col > sip->si_m_endpos.col))
2638 maxpos = sip->si_m_endpos;
2639 if (maxpos_h.lnum == 0
2640 || maxpos_h.lnum > sip->si_h_endpos.lnum
2641 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2642 && maxpos_h.col > sip->si_h_endpos.col))
2643 maxpos_h = sip->si_h_endpos;
2644 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002645 }
2646}
2647
2648/*
2649 * Update an entry in the current_state stack for a start-skip-end pattern.
2650 * This finds the end of the current item, if it's in the current line.
2651 *
2652 * Return the flags for the matched END.
2653 */
2654 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002655update_si_end(
2656 stateitem_T *sip,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002657 int startcol, // where to start searching for the end
2658 int force) // when TRUE overrule a previous end
Bram Moolenaar071d4272004-06-13 20:20:40 +00002659{
2660 lpos_T startpos;
2661 lpos_T endpos;
2662 lpos_T hl_endpos;
2663 lpos_T end_endpos;
2664 int end_idx;
2665
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002666 // return quickly for a keyword
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002667 if (sip->si_idx < 0)
2668 return;
2669
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002670 // Don't update when it's already done. Can be a match of an end pattern
2671 // that started in a previous line. Watch out: can also be a "keepend"
2672 // from a containing item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002673 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2674 return;
2675
2676 /*
2677 * We need to find the end of the region. It may continue in the next
2678 * line.
2679 */
2680 end_idx = 0;
2681 startpos.lnum = current_lnum;
2682 startpos.col = startcol;
2683 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2684 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2685
2686 if (endpos.lnum == 0)
2687 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002688 // No end pattern matched.
Bram Moolenaar860cae12010-06-05 23:22:07 +02002689 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002690 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002691 // a "oneline" never continues in the next line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002692 sip->si_ends = TRUE;
2693 sip->si_m_endpos.lnum = current_lnum;
John Marriottb4ea7712024-08-01 23:01:25 +02002694 sip->si_m_endpos.col = syn_getcurline_len();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002695 }
2696 else
2697 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002698 // continues in the next line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002699 sip->si_ends = FALSE;
2700 sip->si_m_endpos.lnum = 0;
2701 }
2702 sip->si_h_endpos = sip->si_m_endpos;
2703 }
2704 else
2705 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002706 // match within this line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002707 sip->si_m_endpos = endpos;
2708 sip->si_h_endpos = hl_endpos;
2709 sip->si_eoe_pos = end_endpos;
2710 sip->si_ends = TRUE;
2711 sip->si_end_idx = end_idx;
2712 }
2713}
2714
2715/*
2716 * Add a new state to the current state stack.
2717 * It is cleared and the index set to "idx".
2718 * Return FAIL if it's not possible (out of memory).
2719 */
2720 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002721push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002722{
2723 if (ga_grow(&current_state, 1) == FAIL)
2724 return FAIL;
Bram Moolenaara80faa82020-04-12 19:37:17 +02002725 CLEAR_POINTER(&CUR_STATE(current_state.ga_len));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002726 CUR_STATE(current_state.ga_len).si_idx = idx;
2727 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002728 return OK;
2729}
2730
2731/*
2732 * Remove a state from the current_state stack.
2733 */
2734 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002735pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002736{
2737 if (current_state.ga_len)
2738 {
2739 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2740 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002741 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002742 // after the end of a pattern, try matching a keyword or pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00002743 next_match_idx = -1;
2744
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002745 // if first state with "keepend" is popped, reset keepend_level
Bram Moolenaar071d4272004-06-13 20:20:40 +00002746 if (keepend_level >= current_state.ga_len)
2747 keepend_level = -1;
2748}
2749
2750/*
2751 * Find the end of a start/skip/end syntax region after "startpos".
2752 * Only checks one line.
2753 * Also handles a match item that continued from a previous line.
2754 * If not found, the syntax item continues in the next line. m_endpos->lnum
2755 * will be 0.
2756 * If found, the end of the region and the end of the highlighting is
2757 * computed.
2758 */
2759 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002760find_endpos(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002761 int idx, // index of the pattern
2762 lpos_T *startpos, // where to start looking for an END match
2763 lpos_T *m_endpos, // return: end of match
2764 lpos_T *hl_endpos, // return: end of highlighting
2765 long *flagsp, // return: flags of matching END
2766 lpos_T *end_endpos, // return: end of end pattern match
2767 int *end_idx, // return: group ID for end pat. match, or 0
2768 reg_extmatch_T *start_ext) // submatches from the start pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00002769{
2770 colnr_T matchcol;
2771 synpat_T *spp, *spp_skip;
2772 int start_idx;
2773 int best_idx;
2774 regmmatch_T regmatch;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002775 regmmatch_T best_regmatch; // startpos/endpos of best match
Bram Moolenaar071d4272004-06-13 20:20:40 +00002776 lpos_T pos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002777 int had_match = FALSE;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002778 char_u buf_chartab[32]; // chartab array for syn option iskyeyword
Bram Moolenaar071d4272004-06-13 20:20:40 +00002779
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002780 // just in case we are invoked for a keyword
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002781 if (idx < 0)
2782 return;
2783
Bram Moolenaar071d4272004-06-13 20:20:40 +00002784 /*
2785 * Check for being called with a START pattern.
2786 * Can happen with a match that continues to the next line, because it
2787 * contained a region.
2788 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002789 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002790 if (spp->sp_type != SPTYPE_START)
2791 {
2792 *hl_endpos = *startpos;
2793 return;
2794 }
2795
2796 /*
2797 * Find the SKIP or first END pattern after the last START pattern.
2798 */
2799 for (;;)
2800 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002801 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002802 if (spp->sp_type != SPTYPE_START)
2803 break;
2804 ++idx;
2805 }
2806
2807 /*
2808 * Lookup the SKIP pattern (if present)
2809 */
2810 if (spp->sp_type == SPTYPE_SKIP)
2811 {
2812 spp_skip = spp;
2813 ++idx;
2814 }
2815 else
2816 spp_skip = NULL;
2817
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002818 // Setup external matches for syn_regexec().
Bram Moolenaar071d4272004-06-13 20:20:40 +00002819 unref_extmatch(re_extmatch_in);
2820 re_extmatch_in = ref_extmatch(start_ext);
2821
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002822 matchcol = startpos->col; // start looking for a match at sstart
2823 start_idx = idx; // remember the first END pattern.
2824 best_regmatch.startpos[0].col = 0; // avoid compiler warning
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002825
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002826 // use syntax iskeyword option
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002827 save_chartab(buf_chartab);
2828
Bram Moolenaar071d4272004-06-13 20:20:40 +00002829 for (;;)
2830 {
2831 /*
2832 * Find end pattern that matches first after "matchcol".
2833 */
2834 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002835 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002836 {
2837 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002838 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002839
Bram Moolenaar860cae12010-06-05 23:22:07 +02002840 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002841 if (spp->sp_type != SPTYPE_END) // past last END pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00002842 break;
2843 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2844 if (lc_col < 0)
2845 lc_col = 0;
2846
2847 regmatch.rmm_ic = spp->sp_ic;
2848 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002849 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
2850 IF_SYN_TIME(&spp->sp_time));
2851 spp->sp_prog = regmatch.regprog;
2852 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002853 {
2854 if (best_idx == -1 || regmatch.startpos[0].col
2855 < best_regmatch.startpos[0].col)
2856 {
2857 best_idx = idx;
2858 best_regmatch.startpos[0] = regmatch.startpos[0];
2859 best_regmatch.endpos[0] = regmatch.endpos[0];
2860 }
2861 }
2862 }
2863
2864 /*
2865 * If all end patterns have been tried, and there is no match, the
2866 * item continues until end-of-line.
2867 */
2868 if (best_idx == -1)
2869 break;
2870
2871 /*
2872 * If the skip pattern matches before the end pattern,
2873 * continue searching after the skip pattern.
2874 */
2875 if (spp_skip != NULL)
2876 {
2877 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002878 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002879
2880 if (lc_col < 0)
2881 lc_col = 0;
2882 regmatch.rmm_ic = spp_skip->sp_ic;
2883 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002884 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
2885 IF_SYN_TIME(&spp_skip->sp_time));
2886 spp_skip->sp_prog = regmatch.regprog;
2887 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00002888 <= best_regmatch.startpos[0].col)
2889 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01002890 int line_len;
2891
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002892 // Add offset to skip pattern match
Bram Moolenaar071d4272004-06-13 20:20:40 +00002893 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2894
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002895 // If the skip pattern goes on to the next line, there is no
2896 // match with an end pattern in this line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002897 if (pos.lnum > startpos->lnum)
2898 break;
2899
zeertzjq94b7c322024-03-12 21:50:32 +01002900 line_len = ml_get_buf_len(syn_buf, startpos->lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002901
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002902 // take care of an empty match or negative offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00002903 if (pos.col <= matchcol)
2904 ++matchcol;
2905 else if (pos.col <= regmatch.endpos[0].col)
2906 matchcol = pos.col;
2907 else
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002908 // Be careful not to jump over the NUL at the end-of-line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002909 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01002910 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002911 ++matchcol)
2912 ;
2913
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002914 // if the skip pattern includes end-of-line, break here
Bram Moolenaar04bff882016-01-05 20:46:16 +01002915 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002916 break;
2917
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002918 continue; // start with first end pattern again
Bram Moolenaar071d4272004-06-13 20:20:40 +00002919 }
2920 }
2921
2922 /*
2923 * Match from start pattern to end pattern.
2924 * Correct for match and highlight offset of end pattern.
2925 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002926 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002927 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002928 // can't end before the start
Bram Moolenaar071d4272004-06-13 20:20:40 +00002929 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
2930 m_endpos->col = startpos->col;
2931
2932 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_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 (end_endpos->lnum == startpos->lnum
2935 && end_endpos->col < startpos->col)
2936 end_endpos->col = startpos->col;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002937 // can't end after the match
Bram Moolenaar071d4272004-06-13 20:20:40 +00002938 limit_pos(end_endpos, m_endpos);
2939
2940 /*
2941 * If the end group is highlighted differently, adjust the pointers.
2942 */
2943 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
2944 {
2945 *end_idx = best_idx;
2946 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
2947 {
2948 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
2949 hl_endpos->col = best_regmatch.endpos[0].col;
2950 }
2951 else
2952 {
2953 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
2954 hl_endpos->col = best_regmatch.startpos[0].col;
2955 }
2956 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
2957
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002958 // can't end before the start
Bram Moolenaar071d4272004-06-13 20:20:40 +00002959 if (hl_endpos->lnum == startpos->lnum
2960 && hl_endpos->col < startpos->col)
2961 hl_endpos->col = startpos->col;
2962 limit_pos(hl_endpos, m_endpos);
2963
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002964 // now the match ends where the highlighting ends, it is turned
2965 // into the matchgroup for the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00002966 *m_endpos = *hl_endpos;
2967 }
2968 else
2969 {
2970 *end_idx = 0;
2971 *hl_endpos = *end_endpos;
2972 }
2973
2974 *flagsp = spp->sp_flags;
2975
2976 had_match = TRUE;
2977 break;
2978 }
2979
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002980 // no match for an END pattern in this line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002981 if (!had_match)
2982 m_endpos->lnum = 0;
2983
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002984 restore_chartab(buf_chartab);
2985
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01002986 // Remove external matches.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002987 unref_extmatch(re_extmatch_in);
2988 re_extmatch_in = NULL;
2989}
2990
2991/*
2992 * Limit "pos" not to be after "limit".
2993 */
2994 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002995limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002996{
2997 if (pos->lnum > limit->lnum)
2998 *pos = *limit;
2999 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3000 pos->col = limit->col;
3001}
3002
3003/*
3004 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3005 */
3006 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003007limit_pos_zero(
3008 lpos_T *pos,
3009 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003010{
3011 if (pos->lnum == 0)
3012 *pos = *limit;
3013 else
3014 limit_pos(pos, limit);
3015}
3016
3017/*
3018 * Add offset to matched text for end of match or highlight.
3019 */
3020 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003021syn_add_end_off(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003022 lpos_T *result, // returned position
3023 regmmatch_T *regmatch, // start/end of match
3024 synpat_T *spp, // matched pattern
3025 int idx, // index of offset
3026 int extra) // extra chars for offset to start
Bram Moolenaar071d4272004-06-13 20:20:40 +00003027{
3028 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003029 int off;
3030 char_u *base;
3031 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003032
3033 if (spp->sp_off_flags & (1 << idx))
3034 {
3035 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003036 col = regmatch->startpos[0].col;
3037 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003038 }
3039 else
3040 {
3041 result->lnum = regmatch->endpos[0].lnum;
3042 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003043 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003044 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003045 // Don't go past the end of the line. Matters for "rs=e+2" when there
3046 // is a matchgroup. Watch out for match with last NL in the buffer.
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003047 if (result->lnum > syn_buf->b_ml.ml_line_count)
3048 col = 0;
3049 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003050 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003051 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3052 p = base + col;
3053 if (off > 0)
3054 {
3055 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003056 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003057 }
3058 else if (off < 0)
3059 {
3060 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003061 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003062 }
3063 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003064 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003065 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003066}
3067
3068/*
3069 * Add offset to matched text for start of match or highlight.
3070 * Avoid resulting column to become negative.
3071 */
3072 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003073syn_add_start_off(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003074 lpos_T *result, // returned position
3075 regmmatch_T *regmatch, // start/end of match
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003076 synpat_T *spp,
3077 int idx,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003078 int extra) // extra chars for offset to end
Bram Moolenaar071d4272004-06-13 20:20:40 +00003079{
3080 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003081 int off;
3082 char_u *base;
3083 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003084
3085 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3086 {
3087 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003088 col = regmatch->endpos[0].col;
3089 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003090 }
3091 else
3092 {
3093 result->lnum = regmatch->startpos[0].lnum;
3094 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003095 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003096 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003097 if (result->lnum > syn_buf->b_ml.ml_line_count)
3098 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003099 // a "\n" at the end of the pattern may take us below the last line
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003100 result->lnum = syn_buf->b_ml.ml_line_count;
zeertzjq94b7c322024-03-12 21:50:32 +01003101 col = ml_get_buf_len(syn_buf, result->lnum);
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003102 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003103 if (off != 0)
3104 {
3105 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3106 p = base + col;
3107 if (off > 0)
3108 {
3109 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003110 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003111 }
3112 else if (off < 0)
3113 {
3114 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003115 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003116 }
3117 col = (int)(p - base);
3118 }
3119 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003120}
3121
3122/*
3123 * Get current line in syntax buffer.
3124 */
3125 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003126syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003127{
3128 return ml_get_buf(syn_buf, current_lnum, FALSE);
3129}
3130
3131/*
John Marriottb4ea7712024-08-01 23:01:25 +02003132 * Get length of current line in syntax buffer.
3133 */
3134 static colnr_T
3135syn_getcurline_len(void)
3136{
3137 return ml_get_buf_len(syn_buf, current_lnum);
3138}
3139
3140/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003141 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003142 * Returns TRUE when there is a match.
3143 */
3144 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003145syn_regexec(
3146 regmmatch_T *rmp,
3147 linenr_T lnum,
3148 colnr_T col,
3149 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003150{
Bram Moolenaar6f0cf622022-06-19 12:27:45 +01003151 int r;
3152 int timed_out = FALSE;
Bram Moolenaarf7512552013-06-06 14:55:19 +02003153#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003154 proftime_T pt;
3155
3156 if (syn_time_on)
3157 profile_start(&pt);
3158#endif
3159
Bram Moolenaarbcf94422018-06-23 14:21:42 +02003160 if (rmp->regprog == NULL)
3161 // This can happen if a previous call to vim_regexec_multi() tried to
3162 // use the NFA engine, which resulted in NFA_TOO_EXPENSIVE, and
3163 // compiling the pattern with the other engine fails.
3164 return FALSE;
3165
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003166 rmp->rmm_maxcol = syn_buf->b_p_smc;
Paul Ollis65745772022-06-05 16:55:54 +01003167 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, &timed_out);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003168
Bram Moolenaarf7512552013-06-06 14:55:19 +02003169#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003170 if (syn_time_on)
3171 {
3172 profile_end(&pt);
3173 profile_add(&st->total, &pt);
3174 if (profile_cmp(&pt, &st->slowest) < 0)
3175 st->slowest = pt;
3176 ++st->count;
3177 if (r > 0)
3178 ++st->match;
3179 }
3180#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003181#ifdef FEAT_RELTIME
Bram Moolenaar6f0cf622022-06-19 12:27:45 +01003182 if (timed_out && redrawtime_limit_set && !syn_win->w_s->b_syn_slow)
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003183 {
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003184 syn_win->w_s->b_syn_slow = TRUE;
Bram Moolenaar32526b32019-01-19 17:43:09 +01003185 msg(_("'redrawtime' exceeded, syntax highlighting disabled"));
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003186 }
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003187#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003188
3189 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003190 {
3191 rmp->startpos[0].lnum += lnum;
3192 rmp->endpos[0].lnum += lnum;
3193 return TRUE;
3194 }
3195 return FALSE;
3196}
3197
3198/*
3199 * Check one position in a line for a matching keyword.
3200 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003201 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003202 */
3203 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003204check_keyword_id(
3205 char_u *line,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003206 int startcol, // position in line to check for keyword
3207 int *endcolp, // return: character after found keyword
3208 long *flagsp, // return: flags of matching keyword
3209 short **next_listp, // return: next_list of matching keyword
3210 stateitem_T *cur_si, // item at the top of the stack
3211 int *ccharp UNUSED) // conceal substitution char
Bram Moolenaar071d4272004-06-13 20:20:40 +00003212{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003213 keyentry_T *kp;
3214 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003215 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003216 int kwlen;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003217 char_u keyword[MAXKEYWLEN + 1]; // assume max. keyword len is 80
Bram Moolenaardad6b692005-01-25 22:14:34 +00003218 hashtab_T *ht;
3219 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003220
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003221 // Find first character after the keyword. First character was already
3222 // checked.
Bram Moolenaardad6b692005-01-25 22:14:34 +00003223 kwp = line + startcol;
3224 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003225 do
3226 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003227 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003228 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003229 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00003230 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003231 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003232 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003233
Bram Moolenaardad6b692005-01-25 22:14:34 +00003234 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003235 return 0;
3236
3237 /*
3238 * Must make a copy of the keyword, so we can add a NUL and make it
3239 * lowercase.
3240 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003241 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003242
3243 /*
3244 * Try twice:
3245 * 1. matching case
3246 * 2. ignoring case
3247 */
3248 for (round = 1; round <= 2; ++round)
3249 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003250 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003251 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003252 continue;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003253 if (round == 2) // ignore case
Bram Moolenaardad6b692005-01-25 22:14:34 +00003254 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003255
3256 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003257 * Find keywords that match. There can be several with different
3258 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003259 * When current_next_list is non-zero accept only that group, otherwise:
3260 * Accept a not-contained keyword at toplevel.
3261 * Accept a keyword at other levels only if it is in the contains list.
3262 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003263 hi = hash_find(ht, keyword);
3264 if (!HASHITEM_EMPTY(hi))
3265 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003266 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003267 if (current_next_list != 0
3268 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3269 : (cur_si == NULL
3270 ? !(kp->flags & HL_CONTAINED)
3271 : in_id_list(cur_si, cur_si->si_cont_list,
Theodore Duboisf50d5362025-02-05 23:59:25 +01003272 &kp->k_syn, kp->flags)))
Bram Moolenaardad6b692005-01-25 22:14:34 +00003273 {
3274 *endcolp = startcol + kwlen;
3275 *flagsp = kp->flags;
3276 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003277#ifdef FEAT_CONCEAL
3278 *ccharp = kp->k_char;
3279#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003280 return kp->k_syn.id;
3281 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003282 }
3283 }
3284 return 0;
3285}
3286
3287/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003288 * Handle ":syntax conceal" command.
3289 */
3290 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003291syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003292{
3293#ifdef FEAT_CONCEAL
3294 char_u *arg = eap->arg;
3295 char_u *next;
3296
3297 eap->nextcmd = find_nextcmd(arg);
3298 if (eap->skip)
3299 return;
3300
3301 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003302 if (*arg == NUL)
3303 {
3304 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003305 msg("syntax conceal on");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003306 else
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003307 msg("syntax conceal off");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003308 }
3309 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003310 curwin->w_s->b_syn_conceal = TRUE;
3311 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3312 curwin->w_s->b_syn_conceal = FALSE;
3313 else
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003314 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003315#endif
3316}
3317
3318/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003319 * Handle ":syntax case" command.
3320 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003321 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003322syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003323{
3324 char_u *arg = eap->arg;
3325 char_u *next;
3326
3327 eap->nextcmd = find_nextcmd(arg);
3328 if (eap->skip)
3329 return;
3330
3331 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003332 if (*arg == NUL)
3333 {
3334 if (curwin->w_s->b_syn_ic)
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003335 msg("syntax case ignore");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003336 else
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003337 msg("syntax case match");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003338 }
3339 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003340 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003341 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003342 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003343 else
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003344 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003345}
3346
3347/*
3348 * Handle ":syntax foldlevel" command.
3349 */
3350 static void
3351syn_cmd_foldlevel(exarg_T *eap, int syncing UNUSED)
3352{
3353 char_u *arg = eap->arg;
3354 char_u *arg_end;
3355
3356 eap->nextcmd = find_nextcmd(arg);
3357 if (eap->skip)
3358 return;
3359
3360 if (*arg == NUL)
3361 {
3362 switch (curwin->w_s->b_syn_foldlevel)
3363 {
Dominique Pellecd53eed2022-02-05 18:53:06 +00003364 case SYNFLD_START: msg("syntax foldlevel start"); break;
3365 case SYNFLD_MINIMUM: msg("syntax foldlevel minimum"); break;
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003366 default: break;
3367 }
3368 return;
3369 }
3370
3371 arg_end = skiptowhite(arg);
3372 if (STRNICMP(arg, "start", 5) == 0 && arg_end - arg == 5)
3373 curwin->w_s->b_syn_foldlevel = SYNFLD_START;
3374 else if (STRNICMP(arg, "minimum", 7) == 0 && arg_end - arg == 7)
3375 curwin->w_s->b_syn_foldlevel = SYNFLD_MINIMUM;
3376 else
3377 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003378 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003379 return;
3380 }
3381
3382 arg = skipwhite(arg_end);
3383 if (*arg != NUL)
3384 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003385 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003386 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003387}
3388
3389/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003390 * Handle ":syntax spell" command.
3391 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003392 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003393syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003394{
3395 char_u *arg = eap->arg;
3396 char_u *next;
3397
3398 eap->nextcmd = find_nextcmd(arg);
3399 if (eap->skip)
3400 return;
3401
3402 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003403 if (*arg == NUL)
3404 {
3405 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
Dominique Pellecd53eed2022-02-05 18:53:06 +00003406 msg("syntax spell toplevel");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003407 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
Dominique Pellecd53eed2022-02-05 18:53:06 +00003408 msg("syntax spell notoplevel");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003409 else
Dominique Pellecd53eed2022-02-05 18:53:06 +00003410 msg("syntax spell default");
Bram Moolenaarde318c52017-01-17 16:27:10 +01003411 }
3412 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003413 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003414 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003415 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003416 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003417 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003418 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003419 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00003420 semsg(_(e_illegal_argument_str_2), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003421 return;
3422 }
3423
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003424 // assume spell checking changed, force a redraw
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003425 redraw_win_later(curwin, UPD_NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003426}
3427
3428/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003429 * Handle ":syntax iskeyword" command.
3430 */
3431 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003432syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003433{
3434 char_u *arg = eap->arg;
3435 char_u save_chartab[32];
3436 char_u *save_isk;
3437
3438 if (eap->skip)
3439 return;
3440
3441 arg = skipwhite(arg);
3442 if (*arg == NUL)
3443 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003444 msg_puts("\n");
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003445 if (curwin->w_s->b_syn_isk != empty_option)
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003446 {
Bram Moolenaar0c1550d2022-02-06 11:41:57 +00003447 msg_puts("syntax iskeyword ");
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003448 msg_outtrans(curwin->w_s->b_syn_isk);
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003449 }
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003450 else
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003451 msg_outtrans((char_u *)_("syntax iskeyword not set"));
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003452 }
3453 else
3454 {
3455 if (STRNICMP(arg, "clear", 5) == 0)
3456 {
3457 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3458 (size_t)32);
3459 clear_string_option(&curwin->w_s->b_syn_isk);
3460 }
3461 else
3462 {
3463 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3464 save_isk = curbuf->b_p_isk;
3465 curbuf->b_p_isk = vim_strsave(arg);
3466
3467 buf_init_chartab(curbuf, FALSE);
3468 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3469 (size_t)32);
3470 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3471 clear_string_option(&curwin->w_s->b_syn_isk);
3472 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3473 curbuf->b_p_isk = save_isk;
3474 }
3475 }
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003476 redraw_win_later(curwin, UPD_NOT_VALID);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003477}
3478
3479/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003480 * Clear all syntax info for one buffer.
3481 */
3482 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003483syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003484{
3485 int i;
3486
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003487 block->b_syn_error = FALSE; // clear previous error
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003488#ifdef FEAT_RELTIME
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003489 block->b_syn_slow = FALSE; // clear previous timeout
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003490#endif
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003491 block->b_syn_ic = FALSE; // Use case, by default
Bram Moolenaare35a52a2020-05-31 19:48:53 +02003492 block->b_syn_foldlevel = SYNFLD_START;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003493 block->b_syn_spell = SYNSPL_DEFAULT; // default spell checking
Bram Moolenaar860cae12010-06-05 23:22:07 +02003494 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003495#ifdef FEAT_CONCEAL
3496 block->b_syn_conceal = FALSE;
3497#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003498
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003499 // free the keywords
Bram Moolenaar860cae12010-06-05 23:22:07 +02003500 clear_keywtab(&block->b_keywtab);
3501 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003502
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003503 // free the syntax patterns
Bram Moolenaar860cae12010-06-05 23:22:07 +02003504 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3505 syn_clear_pattern(block, i);
3506 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003507
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003508 // free the syntax clusters
Bram Moolenaar860cae12010-06-05 23:22:07 +02003509 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3510 syn_clear_cluster(block, i);
3511 ga_clear(&block->b_syn_clusters);
3512 block->b_spell_cluster_id = 0;
3513 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003514
Bram Moolenaar860cae12010-06-05 23:22:07 +02003515 block->b_syn_sync_flags = 0;
3516 block->b_syn_sync_minlines = 0;
3517 block->b_syn_sync_maxlines = 0;
3518 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003519
Bram Moolenaar473de612013-06-08 18:19:48 +02003520 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003521 block->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003522 VIM_CLEAR(block->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003523#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003524 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003525#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003526 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003527
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003528 // free the stored states
Bram Moolenaar860cae12010-06-05 23:22:07 +02003529 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003530 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003531
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003532 // Reset the counter for ":syn include"
Bram Moolenaar42431a72011-04-01 14:44:59 +02003533 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003534}
3535
3536/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003537 * Get rid of ownsyntax for window "wp".
3538 */
3539 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003540reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003541{
3542 if (wp->w_s != &wp->w_buffer->b_s)
3543 {
3544 syntax_clear(wp->w_s);
3545 vim_free(wp->w_s);
3546 wp->w_s = &wp->w_buffer->b_s;
3547 }
3548}
3549
3550/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003551 * Clear syncing info for one buffer.
3552 */
3553 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003554syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003555{
3556 int i;
3557
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003558 // free the syntax patterns
Bram Moolenaar860cae12010-06-05 23:22:07 +02003559 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3560 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3561 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003562
Bram Moolenaar860cae12010-06-05 23:22:07 +02003563 curwin->w_s->b_syn_sync_flags = 0;
3564 curwin->w_s->b_syn_sync_minlines = 0;
3565 curwin->w_s->b_syn_sync_maxlines = 0;
3566 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003567
Bram Moolenaar473de612013-06-08 18:19:48 +02003568 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003569 curwin->w_s->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003570 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003571 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003572
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003573 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003574}
3575
3576/*
3577 * Remove one pattern from the buffer's pattern list.
3578 */
3579 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003580syn_remove_pattern(
3581 synblock_T *block,
3582 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003583{
3584 synpat_T *spp;
3585
Bram Moolenaar860cae12010-06-05 23:22:07 +02003586 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003587#ifdef FEAT_FOLDING
3588 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003589 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003590#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003591 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003592 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003593 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3594 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003595}
3596
3597/*
3598 * Clear and free one syntax pattern. When clearing all, must be called from
3599 * last to first!
3600 */
3601 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003602syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003603{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003604 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003605 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003606 // Only free sp_cont_list and sp_next_list of first start pattern
Bram Moolenaar860cae12010-06-05 23:22:07 +02003607 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003608 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003609 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3610 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3611 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003612 }
3613}
3614
3615/*
3616 * Clear and free one syntax cluster.
3617 */
3618 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003619syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003620{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003621 vim_free(SYN_CLSTR(block)[i].scl_name);
3622 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3623 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003624}
3625
3626/*
3627 * Handle ":syntax clear" command.
3628 */
3629 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003630syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003631{
3632 char_u *arg = eap->arg;
3633 char_u *arg_end;
3634 int id;
3635
3636 eap->nextcmd = find_nextcmd(arg);
3637 if (eap->skip)
3638 return;
3639
3640 /*
3641 * We have to disable this within ":syn include @group filename",
3642 * because otherwise @group would get deleted.
3643 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3644 * clear".
3645 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003646 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003647 return;
3648
Bram Moolenaar1966c242020-04-20 22:42:32 +02003649 if (ends_excmd2(eap->cmd, arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003650 {
3651 /*
3652 * No argument: Clear all syntax items.
3653 */
3654 if (syncing)
3655 syntax_sync_clear();
3656 else
3657 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003658 syntax_clear(curwin->w_s);
3659 if (curwin->w_s == &curwin->w_buffer->b_s)
3660 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003661 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003662 }
3663 }
3664 else
3665 {
3666 /*
3667 * Clear the group IDs that are in the argument.
3668 */
Bram Moolenaar1966c242020-04-20 22:42:32 +02003669 while (!ends_excmd2(eap->cmd, arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003670 {
3671 arg_end = skiptowhite(arg);
3672 if (*arg == '@')
3673 {
3674 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3675 if (id == 0)
3676 {
Bram Moolenaara9fa8c52023-01-02 18:10:04 +00003677 semsg(_(e_no_such_syntax_cluster_str_1), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003678 break;
3679 }
3680 else
3681 {
3682 /*
3683 * We can't physically delete a cluster without changing
3684 * the IDs of other clusters, so we do the next best thing
3685 * and make it empty.
3686 */
3687 short scl_id = id - SYNID_CLUSTER;
3688
Bram Moolenaard23a8232018-02-10 18:45:26 +01003689 VIM_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003690 }
3691 }
3692 else
3693 {
3694 id = syn_namen2id(arg, (int)(arg_end - arg));
3695 if (id == 0)
3696 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +02003697 semsg(_(e_no_such_highlight_group_name_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003698 break;
3699 }
3700 else
3701 syn_clear_one(id, syncing);
3702 }
3703 arg = skipwhite(arg_end);
3704 }
3705 }
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003706 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003707 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003708}
3709
3710/*
3711 * Clear one syntax group for the current buffer.
3712 */
3713 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003714syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003715{
3716 synpat_T *spp;
3717 int idx;
3718
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003719 // Clear keywords only when not ":syn sync clear group-name"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003720 if (!syncing)
3721 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003722 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3723 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003724 }
3725
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003726 // clear the patterns for "id"
Bram Moolenaar860cae12010-06-05 23:22:07 +02003727 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003728 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003729 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003730 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3731 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003732 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003733 }
3734}
3735
3736/*
3737 * Handle ":syntax on" command.
3738 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003739 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003740syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003741{
3742 syn_cmd_onoff(eap, "syntax");
3743}
3744
3745/*
3746 * Handle ":syntax enable" command.
3747 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003748 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003749syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003750{
Bram Moolenaarc8a9fe52021-11-20 19:50:59 +00003751 set_internal_string_var((char_u *)"g:syntax_cmd", (char_u *)"enable");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003752 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003753 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003754}
3755
3756/*
3757 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003758 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003759 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003760 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003761syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003762{
Bram Moolenaar63b91732021-08-05 20:40:03 +02003763 set_nextcmd(eap, eap->arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003764 if (!eap->skip)
3765 {
Bram Moolenaarc8a9fe52021-11-20 19:50:59 +00003766 set_internal_string_var((char_u *)"g:syntax_cmd", (char_u *)"reset");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003767 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003768 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003769 }
3770}
3771
3772/*
3773 * Handle ":syntax manual" command.
3774 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003775 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003776syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003777{
3778 syn_cmd_onoff(eap, "manual");
3779}
3780
3781/*
3782 * Handle ":syntax off" command.
3783 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003784 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003785syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003786{
3787 syn_cmd_onoff(eap, "nosyntax");
3788}
3789
3790 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003791syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003792{
3793 char_u buf[100];
3794
Bram Moolenaar63b91732021-08-05 20:40:03 +02003795 set_nextcmd(eap, eap->arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003796 if (!eap->skip)
3797 {
3798 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003799 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003800 do_cmdline_cmd(buf);
3801 }
3802}
3803
3804/*
3805 * Handle ":syntax [list]" command: list current syntax words.
3806 */
3807 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003808syn_cmd_list(
3809 exarg_T *eap,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003810 int syncing) // when TRUE: list syncing items
Bram Moolenaar071d4272004-06-13 20:20:40 +00003811{
3812 char_u *arg = eap->arg;
3813 int id;
3814 char_u *arg_end;
3815
3816 eap->nextcmd = find_nextcmd(arg);
3817 if (eap->skip)
3818 return;
3819
Bram Moolenaar860cae12010-06-05 23:22:07 +02003820 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003821 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003822 msg(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003823 return;
3824 }
3825
3826 if (syncing)
3827 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003828 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003829 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003830 msg_puts(_("syncing on C-style comments"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003831 syn_lines_msg();
3832 syn_match_msg();
3833 return;
3834 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003835 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003836 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003837 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003838 msg_puts(_("no syncing"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003839 else
3840 {
Bram Moolenaar99502802020-11-18 16:53:23 +01003841 if (curwin->w_s->b_syn_sync_minlines == MAXLNUM)
3842 msg_puts(_("syncing starts at the first line"));
3843 else
3844 {
3845 msg_puts(_("syncing starts "));
3846 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3847 msg_puts(_(" lines before top line"));
3848 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003849 syn_match_msg();
3850 }
3851 return;
3852 }
Bram Moolenaar32526b32019-01-19 17:43:09 +01003853 msg_puts_title(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003854 if (curwin->w_s->b_syn_sync_minlines > 0
3855 || curwin->w_s->b_syn_sync_maxlines > 0
3856 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003857 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003858 msg_puts(_("\nsyncing on items"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003859 syn_lines_msg();
3860 syn_match_msg();
3861 }
3862 }
3863 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01003864 msg_puts_title(_("\n--- Syntax items ---"));
Bram Moolenaar1966c242020-04-20 22:42:32 +02003865 if (ends_excmd2(eap->cmd, arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003866 {
3867 /*
3868 * No argument: List all group IDs and all syntax clusters.
3869 */
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02003870 for (id = 1; id <= highlight_num_groups() && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003871 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003872 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003873 syn_list_cluster(id);
3874 }
3875 else
3876 {
3877 /*
3878 * List the group IDs and syntax clusters that are in the argument.
3879 */
Bram Moolenaar1966c242020-04-20 22:42:32 +02003880 while (!ends_excmd2(eap->cmd, arg) && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003881 {
3882 arg_end = skiptowhite(arg);
3883 if (*arg == '@')
3884 {
3885 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3886 if (id == 0)
Bram Moolenaara9fa8c52023-01-02 18:10:04 +00003887 semsg(_(e_no_such_syntax_cluster_str_2), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003888 else
3889 syn_list_cluster(id - SYNID_CLUSTER);
3890 }
3891 else
3892 {
3893 id = syn_namen2id(arg, (int)(arg_end - arg));
3894 if (id == 0)
Bram Moolenaare29a27f2021-07-20 21:07:36 +02003895 semsg(_(e_no_such_highlight_group_name_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003896 else
3897 syn_list_one(id, syncing, TRUE);
3898 }
3899 arg = skipwhite(arg_end);
3900 }
3901 }
Bram Moolenaar63b91732021-08-05 20:40:03 +02003902 set_nextcmd(eap, arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003903}
3904
3905 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003906syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003907{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003908 if (curwin->w_s->b_syn_sync_maxlines > 0
3909 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003910 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003911 msg_puts("; ");
Bram Moolenaar99502802020-11-18 16:53:23 +01003912 if (curwin->w_s->b_syn_sync_minlines == MAXLNUM)
3913 msg_puts(_("from the first line"));
3914 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003915 {
Bram Moolenaar99502802020-11-18 16:53:23 +01003916 if (curwin->w_s->b_syn_sync_minlines > 0)
3917 {
3918 msg_puts(_("minimal "));
3919 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3920 if (curwin->w_s->b_syn_sync_maxlines)
3921 msg_puts(", ");
3922 }
3923 if (curwin->w_s->b_syn_sync_maxlines > 0)
3924 {
3925 msg_puts(_("maximal "));
3926 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
3927 }
3928 msg_puts(_(" lines before top line"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003929 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003930 }
3931}
3932
3933 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003934syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003935{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003936 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003937 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003938 msg_puts(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003939 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar32526b32019-01-19 17:43:09 +01003940 msg_puts(_(" line breaks"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003941 }
3942}
3943
3944static int last_matchgroup;
3945
John Marriottb4ea7712024-08-01 23:01:25 +02003946static void syn_list_flags(keyvalue_T *nlist, int nr_entries, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003947
3948/*
3949 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3950 */
3951 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003952syn_list_one(
3953 int id,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003954 int syncing, // when TRUE: list syncing items
3955 int link_only) // when TRUE; list link-only too
Bram Moolenaar071d4272004-06-13 20:20:40 +00003956{
3957 int attr;
3958 int idx;
3959 int did_header = FALSE;
3960 synpat_T *spp;
John Marriottb4ea7712024-08-01 23:01:25 +02003961 static keyvalue_T namelist1[] =
Bram Moolenaar071d4272004-06-13 20:20:40 +00003962 {
John Marriottb4ea7712024-08-01 23:01:25 +02003963 KEYVALUE_ENTRY(HL_DISPLAY, "display"),
3964 KEYVALUE_ENTRY(HL_CONTAINED, "contained"),
3965 KEYVALUE_ENTRY(HL_ONELINE, "oneline"),
3966 KEYVALUE_ENTRY(HL_KEEPEND, "keepend"),
3967 KEYVALUE_ENTRY(HL_EXTEND, "extend"),
3968 KEYVALUE_ENTRY(HL_EXCLUDENL, "excludenl"),
3969 KEYVALUE_ENTRY(HL_TRANSP, "transparent"),
3970 KEYVALUE_ENTRY(HL_FOLD, "fold")
Bram Moolenaar860cae12010-06-05 23:22:07 +02003971#ifdef FEAT_CONCEAL
John Marriottb4ea7712024-08-01 23:01:25 +02003972 ,
3973 KEYVALUE_ENTRY(HL_CONCEAL, "conceal"),
3974 KEYVALUE_ENTRY(HL_CONCEALENDS, "concealends")
Bram Moolenaar860cae12010-06-05 23:22:07 +02003975#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003976 };
John Marriottb4ea7712024-08-01 23:01:25 +02003977 static keyvalue_T namelist2[] =
Bram Moolenaar071d4272004-06-13 20:20:40 +00003978 {
John Marriottb4ea7712024-08-01 23:01:25 +02003979 KEYVALUE_ENTRY(HL_SKIPWHITE, "skipwhite"),
3980 KEYVALUE_ENTRY(HL_SKIPNL, "skipnl"),
3981 KEYVALUE_ENTRY(HL_SKIPEMPTY, "skipempty")
Bram Moolenaar071d4272004-06-13 20:20:40 +00003982 };
3983
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003984 attr = HL_ATTR(HLF_D); // highlight like directories
Bram Moolenaar071d4272004-06-13 20:20:40 +00003985
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003986 // list the keywords for "id"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003987 if (!syncing)
3988 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003989 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3990 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003991 did_header, attr);
3992 }
3993
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01003994 // list the patterns for "id"
Bram Moolenaar860cae12010-06-05 23:22:07 +02003995 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003996 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003997 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003998 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3999 continue;
4000
4001 (void)syn_list_header(did_header, 999, id);
4002 did_header = TRUE;
4003 last_matchgroup = 0;
4004 if (spp->sp_type == SPTYPE_MATCH)
4005 {
4006 put_pattern("match", ' ', spp, attr);
4007 msg_putchar(' ');
4008 }
4009 else if (spp->sp_type == SPTYPE_START)
4010 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004011 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4012 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4013 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4014 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4015 while (idx < curwin->w_s->b_syn_patterns.ga_len
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004016 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004017 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004018 --idx;
4019 msg_putchar(' ');
4020 }
John Marriottb4ea7712024-08-01 23:01:25 +02004021 syn_list_flags(namelist1, (int)ARRAY_LENGTH(namelist1), spp->sp_flags, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004022
4023 if (spp->sp_cont_list != NULL)
4024 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4025
4026 if (spp->sp_syn.cont_in_list != NULL)
4027 put_id_list((char_u *)"containedin",
4028 spp->sp_syn.cont_in_list, attr);
4029
4030 if (spp->sp_next_list != NULL)
4031 {
4032 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
John Marriottb4ea7712024-08-01 23:01:25 +02004033 syn_list_flags(namelist2, (int)ARRAY_LENGTH(namelist2), spp->sp_flags, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004034 }
4035 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4036 {
4037 if (spp->sp_flags & HL_SYNC_HERE)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004038 msg_puts_attr("grouphere", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004039 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004040 msg_puts_attr("groupthere", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004041 msg_putchar(' ');
4042 if (spp->sp_sync_idx >= 0)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004043 msg_outtrans(highlight_group_name(SYN_ITEMS(curwin->w_s)
4044 [spp->sp_sync_idx].sp_syn.id - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004045 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004046 msg_puts("NONE");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004047 msg_putchar(' ');
4048 }
4049 }
4050
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004051 // list the link, if there is one
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004052 if (highlight_link_id(id - 1) && (did_header || link_only) && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004053 {
4054 (void)syn_list_header(did_header, 999, id);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004055 msg_puts_attr("links to", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004056 msg_putchar(' ');
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004057 msg_outtrans(highlight_group_name(highlight_link_id(id - 1) - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004058 }
4059}
4060
4061 static void
John Marriottb4ea7712024-08-01 23:01:25 +02004062syn_list_flags(keyvalue_T *nlist, int nr_entries, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004063{
4064 int i;
4065
John Marriottb4ea7712024-08-01 23:01:25 +02004066 for (i = 0; i < nr_entries; ++i)
4067 if (flags & nlist[i].key)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004068 {
John Marriott8d4477e2024-11-02 15:59:01 +01004069 msg_puts_attr((char *)nlist[i].value.string, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004070 msg_putchar(' ');
4071 }
4072}
4073
4074/*
4075 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4076 */
4077 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004078syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004079{
4080 int endcol = 15;
4081
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004082 // slight hack: roughly duplicate the guts of syn_list_header()
Bram Moolenaar071d4272004-06-13 20:20:40 +00004083 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004084 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004085
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004086 if (msg_col >= endcol) // output at least one space
Bram Moolenaar071d4272004-06-13 20:20:40 +00004087 endcol = msg_col + 1;
Christian Brabandt220474d2024-07-20 13:26:44 +02004088 if (Columns <= (long)endcol) // avoid hang for tiny window
4089 endcol = (int)(Columns - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004090
4091 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004092 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004093 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004094 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004095 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004096 }
4097 else
4098 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004099 msg_puts_attr("cluster", HL_ATTR(HLF_D));
4100 msg_puts("=NONE");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004101 }
4102}
4103
4104 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004105put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004106{
4107 short *p;
4108
Bram Moolenaar32526b32019-01-19 17:43:09 +01004109 msg_puts_attr((char *)name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004110 msg_putchar('=');
4111 for (p = list; *p; ++p)
4112 {
4113 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4114 {
4115 if (p[1])
Bram Moolenaar32526b32019-01-19 17:43:09 +01004116 msg_puts("ALLBUT");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004117 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004118 msg_puts("ALL");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004119 }
4120 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4121 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004122 msg_puts("TOP");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004123 }
4124 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4125 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004126 msg_puts("CONTAINED");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004127 }
4128 else if (*p >= SYNID_CLUSTER)
4129 {
4130 short scl_id = *p - SYNID_CLUSTER;
4131
4132 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004133 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004134 }
4135 else
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004136 msg_outtrans(highlight_group_name(*p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004137 if (p[1])
4138 msg_putchar(',');
4139 }
4140 msg_putchar(' ');
4141}
4142
4143 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004144put_pattern(
4145 char *s,
4146 int c,
4147 synpat_T *spp,
4148 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004149{
4150 long n;
4151 int mask;
4152 int first;
4153 static char *sepchars = "/+=-#@\"|'^&";
4154 int i;
4155
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004156 // May have to write "matchgroup=group"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004157 if (last_matchgroup != spp->sp_syn_match_id)
4158 {
4159 last_matchgroup = spp->sp_syn_match_id;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004160 msg_puts_attr("matchgroup", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004161 msg_putchar('=');
4162 if (last_matchgroup == 0)
4163 msg_outtrans((char_u *)"NONE");
4164 else
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02004165 msg_outtrans(highlight_group_name(last_matchgroup - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004166 msg_putchar(' ');
4167 }
4168
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004169 // output the name of the pattern and an '=' or ' '
Bram Moolenaar32526b32019-01-19 17:43:09 +01004170 msg_puts_attr(s, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004171 msg_putchar(c);
4172
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004173 // output the pattern, in between a char that is not in the pattern
Bram Moolenaar071d4272004-06-13 20:20:40 +00004174 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4175 if (sepchars[++i] == NUL)
4176 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004177 i = 0; // no good char found, just use the first one
Bram Moolenaar071d4272004-06-13 20:20:40 +00004178 break;
4179 }
4180 msg_putchar(sepchars[i]);
4181 msg_outtrans(spp->sp_pattern);
4182 msg_putchar(sepchars[i]);
4183
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004184 // output any pattern options
Bram Moolenaar071d4272004-06-13 20:20:40 +00004185 first = TRUE;
4186 for (i = 0; i < SPO_COUNT; ++i)
4187 {
4188 mask = (1 << i);
4189 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4190 {
4191 if (!first)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004192 msg_putchar(','); // separate with commas
Bram Moolenaar32526b32019-01-19 17:43:09 +01004193 msg_puts(spo_name_tab[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004194 n = spp->sp_offsets[i];
4195 if (i != SPO_LC_OFF)
4196 {
4197 if (spp->sp_off_flags & mask)
4198 msg_putchar('s');
4199 else
4200 msg_putchar('e');
4201 if (n > 0)
4202 msg_putchar('+');
4203 }
4204 if (n || i == SPO_LC_OFF)
4205 msg_outnum(n);
4206 first = FALSE;
4207 }
4208 }
4209 msg_putchar(' ');
4210}
4211
4212/*
4213 * List or clear the keywords for one syntax group.
4214 * Return TRUE if the header has been printed.
4215 */
4216 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004217syn_list_keywords(
4218 int id,
4219 hashtab_T *ht,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004220 int did_header, // header has already been printed
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004221 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004222{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004223 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004224 hashitem_T *hi;
4225 keyentry_T *kp;
4226 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004227 int prev_contained = 0;
4228 short *prev_next_list = NULL;
4229 short *prev_cont_in_list = NULL;
4230 int prev_skipnl = 0;
4231 int prev_skipwhite = 0;
4232 int prev_skipempty = 0;
4233
Bram Moolenaar071d4272004-06-13 20:20:40 +00004234 /*
4235 * Unfortunately, this list of keywords is not sorted on alphabet but on
4236 * hash value...
4237 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004238 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004239 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004240 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004241 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004242 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004243 --todo;
4244 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004245 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004246 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004247 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004248 if (prev_contained != (kp->flags & HL_CONTAINED)
4249 || prev_skipnl != (kp->flags & HL_SKIPNL)
4250 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4251 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4252 || prev_cont_in_list != kp->k_syn.cont_in_list
4253 || prev_next_list != kp->next_list)
4254 outlen = 9999;
4255 else
4256 outlen = (int)STRLEN(kp->keyword);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004257 // output "contained" and "nextgroup" on each line
Bram Moolenaardad6b692005-01-25 22:14:34 +00004258 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004259 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004260 prev_contained = 0;
4261 prev_next_list = NULL;
4262 prev_cont_in_list = NULL;
4263 prev_skipnl = 0;
4264 prev_skipwhite = 0;
4265 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004266 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004267 did_header = TRUE;
4268 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004269 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004270 msg_puts_attr("contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004271 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004272 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004273 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004274 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004275 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004276 put_id_list((char_u *)"containedin",
4277 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004278 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004279 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004280 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004281 if (kp->next_list != prev_next_list)
4282 {
4283 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4284 msg_putchar(' ');
4285 prev_next_list = kp->next_list;
4286 if (kp->flags & HL_SKIPNL)
4287 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004288 msg_puts_attr("skipnl", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004289 msg_putchar(' ');
4290 prev_skipnl = (kp->flags & HL_SKIPNL);
4291 }
4292 if (kp->flags & HL_SKIPWHITE)
4293 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004294 msg_puts_attr("skipwhite", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004295 msg_putchar(' ');
4296 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4297 }
4298 if (kp->flags & HL_SKIPEMPTY)
4299 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004300 msg_puts_attr("skipempty", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004301 msg_putchar(' ');
4302 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4303 }
4304 }
4305 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004306 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004307 }
4308 }
4309 }
4310
4311 return did_header;
4312}
4313
4314 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004315syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004316{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004317 hashitem_T *hi;
4318 keyentry_T *kp;
4319 keyentry_T *kp_prev;
4320 keyentry_T *kp_next;
4321 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004322
Bram Moolenaardad6b692005-01-25 22:14:34 +00004323 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004324 todo = (int)ht->ht_used;
Yegappan Lakshmanan14113fd2023-03-07 17:13:51 +00004325 FOR_ALL_HASHTAB_ITEMS(ht, hi, todo)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004326 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004327 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004328 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004329 --todo;
4330 kp_prev = NULL;
4331 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004332 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004333 if (kp->k_syn.id == id)
4334 {
4335 kp_next = kp->ke_next;
4336 if (kp_prev == NULL)
4337 {
4338 if (kp_next == NULL)
Bram Moolenaaref2c3252022-11-25 16:31:51 +00004339 hash_remove(ht, hi, "syntax clear keyword");
Bram Moolenaardad6b692005-01-25 22:14:34 +00004340 else
4341 hi->hi_key = KE2HIKEY(kp_next);
4342 }
4343 else
4344 kp_prev->ke_next = kp_next;
4345 vim_free(kp->next_list);
4346 vim_free(kp->k_syn.cont_in_list);
4347 vim_free(kp);
4348 kp = kp_next;
4349 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004350 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004351 {
4352 kp_prev = kp;
4353 kp = kp->ke_next;
4354 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004355 }
4356 }
4357 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004358 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004359}
4360
4361/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004362 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004363 */
4364 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004365clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004366{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004367 hashitem_T *hi;
4368 int todo;
4369 keyentry_T *kp;
4370 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004371
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004372 todo = (int)ht->ht_used;
Yegappan Lakshmanan14113fd2023-03-07 17:13:51 +00004373 FOR_ALL_HASHTAB_ITEMS(ht, hi, todo)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004374 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004375 if (!HASHITEM_EMPTY(hi))
4376 {
4377 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004378 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004379 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004380 kp_next = kp->ke_next;
4381 vim_free(kp->next_list);
4382 vim_free(kp->k_syn.cont_in_list);
4383 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004384 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004385 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004386 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004387 hash_clear(ht);
4388 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004389}
4390
4391/*
4392 * Add a keyword to the list of keywords.
4393 */
4394 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004395add_keyword(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004396 char_u *name, // name of keyword
John Marriottb4ea7712024-08-01 23:01:25 +02004397 size_t namelen, // length of keyword (excluding the NUL)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004398 int id, // group ID for this keyword
4399 int flags, // flags for this keyword
4400 short *cont_in_list, // containedin for this keyword
4401 short *next_list, // nextgroup for this keyword
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004402 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004403{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004404 keyentry_T *kp;
4405 hashtab_T *ht;
4406 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004407 char_u *name_ic;
John Marriottb4ea7712024-08-01 23:01:25 +02004408 size_t name_iclen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004409 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004410 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004411
Bram Moolenaar860cae12010-06-05 23:22:07 +02004412 if (curwin->w_s->b_syn_ic)
John Marriottb4ea7712024-08-01 23:01:25 +02004413 {
4414 name_ic = str_foldcase(name, (int)namelen,
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004415 name_folded, MAXKEYWLEN + 1);
John Marriottb4ea7712024-08-01 23:01:25 +02004416 name_iclen = STRLEN(name_ic);
4417 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004418 else
John Marriottb4ea7712024-08-01 23:01:25 +02004419 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004420 name_ic = name;
John Marriottb4ea7712024-08-01 23:01:25 +02004421 name_iclen = namelen;
4422 }
4423 kp = alloc(offsetof(keyentry_T, keyword) + name_iclen + 1);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004424 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004425 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004426 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004427 kp->k_syn.id = id;
4428 kp->k_syn.inc_tag = current_syn_inc_tag;
4429 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004430 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004431 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004432 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004433 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004434 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004435
Bram Moolenaar860cae12010-06-05 23:22:07 +02004436 if (curwin->w_s->b_syn_ic)
4437 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004438 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004439 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004440
Bram Moolenaardad6b692005-01-25 22:14:34 +00004441 hash = hash_hash(kp->keyword);
4442 hi = hash_lookup(ht, kp->keyword, hash);
4443 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004444 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004445 // new keyword, add to hashtable
Bram Moolenaardad6b692005-01-25 22:14:34 +00004446 kp->ke_next = NULL;
4447 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004448 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004449 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004450 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004451 // keyword already exists, prepend to list
Bram Moolenaardad6b692005-01-25 22:14:34 +00004452 kp->ke_next = HI2KE(hi);
4453 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004454 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004455}
4456
4457/*
4458 * Get the start and end of the group name argument.
4459 * Return a pointer to the first argument.
4460 * Return NULL if the end of the command was found instead of further args.
4461 */
4462 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004463get_group_name(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004464 char_u *arg, // start of the argument
4465 char_u **name_end) // pointer to end of the name
Bram Moolenaar071d4272004-06-13 20:20:40 +00004466{
4467 char_u *rest;
4468
4469 *name_end = skiptowhite(arg);
4470 rest = skipwhite(*name_end);
4471
4472 /*
4473 * Check if there are enough arguments. The first argument may be a
4474 * pattern, where '|' is allowed, so only check for NUL.
4475 */
4476 if (ends_excmd(*arg) || *rest == NUL)
4477 return NULL;
4478 return rest;
4479}
4480
4481/*
4482 * Check for syntax command option arguments.
4483 * This can be called at any place in the list of arguments, and just picks
4484 * out the arguments that are known. Can be called several times in a row to
4485 * collect all options in between other arguments.
4486 * Return a pointer to the next argument (which isn't an option).
4487 * Return NULL for any error;
4488 */
4489 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004490get_syn_options(
Bram Moolenaar1966c242020-04-20 22:42:32 +02004491 char_u *start, // next argument to be checked
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004492 syn_opt_arg_T *opt, // various things
Bram Moolenaarde318c52017-01-17 16:27:10 +01004493 int *conceal_char UNUSED,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004494 int skip) // TRUE if skipping over command
Bram Moolenaar071d4272004-06-13 20:20:40 +00004495{
Bram Moolenaar1966c242020-04-20 22:42:32 +02004496 char_u *arg = start;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004497 char_u *gname_start, *gname;
4498 int syn_id;
4499 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004500 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004501 int i;
4502 int fidx;
4503 static struct flag
4504 {
4505 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004506 int argtype;
4507 int flags;
4508 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4509 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4510 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4511 {"eExXtTeEnNdD", 0, HL_EXTEND},
4512 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4513 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4514 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4515 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4516 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4517 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4518 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4519 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4520 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004521 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4522 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4523 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004524 {"cCoOnNtTaAiInNsS", 1, 0},
4525 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4526 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004527 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004528 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004529
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004530 if (arg == NULL) // already detected error
Bram Moolenaar071d4272004-06-13 20:20:40 +00004531 return NULL;
4532
Bram Moolenaar860cae12010-06-05 23:22:07 +02004533#ifdef FEAT_CONCEAL
4534 if (curwin->w_s->b_syn_conceal)
4535 opt->flags |= HL_CONCEAL;
4536#endif
4537
Bram Moolenaar071d4272004-06-13 20:20:40 +00004538 for (;;)
4539 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004540 /*
4541 * This is used very often when a large number of keywords is defined.
4542 * Need to skip quickly when no option name is found.
4543 * Also avoid tolower(), it's slow.
4544 */
4545 if (strchr(first_letters, *arg) == NULL)
4546 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004547
K.Takataeeec2542021-06-02 13:28:16 +02004548 for (fidx = ARRAY_LENGTH(flagtab); --fidx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004549 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004550 p = flagtab[fidx].name;
4551 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4552 if (arg[len] != p[i] && arg[len] != p[i + 1])
4553 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004554 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004555 || (flagtab[fidx].argtype > 0
4556 ? arg[len] == '='
Bram Moolenaar1966c242020-04-20 22:42:32 +02004557 : ends_excmd2(start, arg + len))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004558 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004559 if (opt->keyword
4560 && (flagtab[fidx].flags == HL_DISPLAY
4561 || flagtab[fidx].flags == HL_FOLD
4562 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004563 // treat "display", "fold" and "extend" as a keyword
Bram Moolenaar071d4272004-06-13 20:20:40 +00004564 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004565 break;
4566 }
4567 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004568 if (fidx < 0) // no match found
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004569 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004570
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004571 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004572 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004573 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004574 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004575 emsg(_(e_contains_argument_not_accepted_here));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004576 return NULL;
4577 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004578 if (get_id_list(&arg, 8, &opt->cont_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 == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004582 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004583 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004584 return NULL;
4585 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004586 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004587 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004588 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004589 return NULL;
4590 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004591 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4592 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004593 // cchar=?
Bram Moolenaar860cae12010-06-05 23:22:07 +02004594 if (has_mbyte)
4595 {
Bram Moolenaar264b74f2019-01-24 17:18:42 +01004596#ifdef FEAT_CONCEAL
Bram Moolenaar860cae12010-06-05 23:22:07 +02004597 *conceal_char = mb_ptr2char(arg + 6);
Bram Moolenaar264b74f2019-01-24 17:18:42 +01004598#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004599 arg += mb_ptr2len(arg + 6) - 1;
4600 }
4601 else
Bram Moolenaar56be9502010-06-06 14:20:26 +02004602 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004603#ifdef FEAT_CONCEAL
4604 *conceal_char = arg[6];
4605#else
4606 ;
4607#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004608 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004609#ifdef FEAT_CONCEAL
4610 if (!vim_isprintc_strict(*conceal_char))
4611 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00004612 emsg(_(e_invalid_cchar_value));
Bram Moolenaar4124e722011-01-22 00:58:20 +01004613 return NULL;
4614 }
4615#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004616 arg = skipwhite(arg + 7);
4617 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004618 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004619 {
4620 opt->flags |= flagtab[fidx].flags;
4621 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004622
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004623 if (flagtab[fidx].flags == HL_SYNC_HERE
4624 || flagtab[fidx].flags == HL_SYNC_THERE)
4625 {
4626 if (opt->sync_idx == NULL)
4627 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004628 emsg(_(e_groupthere_not_accepted_here));
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004629 return NULL;
4630 }
4631 gname_start = arg;
4632 arg = skiptowhite(arg);
4633 if (gname_start == arg)
4634 return NULL;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02004635 gname = vim_strnsave(gname_start, arg - gname_start);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004636 if (gname == NULL)
4637 return NULL;
4638 if (STRCMP(gname, "NONE") == 0)
4639 *opt->sync_idx = NONE_IDX;
4640 else
4641 {
4642 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004643 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4644 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
Bram Moolenaar71ccd032020-06-12 22:59:11 +02004645 && SYN_ITEMS(curwin->w_s)[i].sp_type
4646 == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004647 {
4648 *opt->sync_idx = i;
4649 break;
4650 }
4651 if (i < 0)
4652 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004653 semsg(_(e_didnt_find_region_item_for_str), gname);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004654 vim_free(gname);
4655 return NULL;
4656 }
4657 }
4658
4659 vim_free(gname);
4660 arg = skipwhite(arg);
4661 }
4662#ifdef FEAT_FOLDING
4663 else if (flagtab[fidx].flags == HL_FOLD
4664 && foldmethodIsSyntax(curwin))
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004665 // Need to update folds later.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004666 foldUpdateAll(curwin);
4667#endif
4668 }
4669 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004670
4671 return arg;
4672}
4673
4674/*
4675 * Adjustments to syntax item when declared in a ":syn include"'d file.
4676 * Set the contained flag, and if the item is not already contained, add it
4677 * to the specified top-level group, if any.
4678 */
4679 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004680syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004681{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004682 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004683 return;
Theodore Duboisf50d5362025-02-05 23:59:25 +01004684 *flagsp |= HL_CONTAINED | HL_INCLUDED_TOPLEVEL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004685 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004686 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004687 // We have to alloc this, because syn_combine_list() will free it.
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004688 short *grp_list = ALLOC_MULT(short, 2);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004689 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004690
4691 if (grp_list != NULL)
4692 {
4693 grp_list[0] = id;
4694 grp_list[1] = 0;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004695 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list,
4696 &grp_list, CLUSTER_ADD);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004697 }
4698 }
4699}
4700
4701/*
4702 * Handle ":syntax include [@{group-name}] filename" command.
4703 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004704 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004705syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004706{
4707 char_u *arg = eap->arg;
4708 int sgl_id = 1;
4709 char_u *group_name_end;
4710 char_u *rest;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004711 char *errormsg = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004712 int prev_toplvl_grp;
4713 int prev_syn_inc_tag;
4714 int source = FALSE;
4715
4716 eap->nextcmd = find_nextcmd(arg);
4717 if (eap->skip)
4718 return;
4719
4720 if (arg[0] == '@')
4721 {
4722 ++arg;
4723 rest = get_group_name(arg, &group_name_end);
4724 if (rest == NULL)
4725 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00004726 emsg(_(e_filename_required));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004727 return;
4728 }
4729 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004730 if (sgl_id == 0)
4731 return;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004732 // separate_nextcmd() and expand_filename() depend on this
Bram Moolenaar071d4272004-06-13 20:20:40 +00004733 eap->arg = rest;
4734 }
4735
4736 /*
4737 * Everything that's left, up to the next command, should be the
4738 * filename to include.
4739 */
Bram Moolenaar8071cb22019-07-12 17:58:01 +02004740 eap->argt |= (EX_XFILE | EX_NOSPC);
Bram Moolenaarac485062022-03-23 19:45:01 +00004741 separate_nextcmd(eap, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004742 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4743 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004744 // For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4745 // file. Need to expand the file name first. In other cases
4746 // ":runtime!" is used.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004747 source = TRUE;
4748 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4749 {
4750 if (errormsg != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004751 emsg(errormsg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004752 return;
4753 }
4754 }
4755
4756 /*
4757 * Save and restore the existing top-level grouplist id and ":syn
4758 * include" tag around the actual inclusion.
4759 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004760 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4761 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00004762 emsg(_(e_too_many_syntax_includes));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004763 return;
4764 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004765 prev_syn_inc_tag = current_syn_inc_tag;
4766 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004767 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4768 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01004769 if (source ? do_source(eap->arg, FALSE, DOSO_NONE, NULL) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004770 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004771 semsg(_(e_cant_open_file_str), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004772 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004773 current_syn_inc_tag = prev_syn_inc_tag;
4774}
4775
4776/*
4777 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4778 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004779 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004780syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004781{
4782 char_u *arg = eap->arg;
4783 char_u *group_name_end;
4784 int syn_id;
4785 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004786 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004787 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004788 char_u *kw;
4789 syn_opt_arg_T syn_opt_arg;
4790 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004791 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004792
4793 rest = get_group_name(arg, &group_name_end);
4794
4795 if (rest != NULL)
4796 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004797 if (eap->skip)
4798 syn_id = -1;
4799 else
4800 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004801 if (syn_id != 0)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004802 // allocate a buffer, for removing backslashes in the keyword
Bram Moolenaar964b3742019-05-24 18:54:09 +02004803 keyword_copy = alloc(STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004804 if (keyword_copy != NULL)
4805 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004806 syn_opt_arg.flags = 0;
4807 syn_opt_arg.keyword = TRUE;
4808 syn_opt_arg.sync_idx = NULL;
4809 syn_opt_arg.has_cont_list = FALSE;
4810 syn_opt_arg.cont_in_list = NULL;
4811 syn_opt_arg.next_list = NULL;
4812
Bram Moolenaar071d4272004-06-13 20:20:40 +00004813 /*
4814 * The options given apply to ALL keywords, so all options must be
4815 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004816 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004817 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004818 cnt = 0;
4819 p = keyword_copy;
Bram Moolenaar1966c242020-04-20 22:42:32 +02004820 for ( ; rest != NULL && !ends_excmd2(eap->arg, rest);
4821 rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004822 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004823 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4824 eap->skip);
Bram Moolenaar1966c242020-04-20 22:42:32 +02004825 if (rest == NULL || ends_excmd2(eap->arg, rest))
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004826 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004827 // Copy the keyword, removing backslashes, and add a NUL.
Bram Moolenaar1c465442017-03-12 20:10:05 +01004828 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004829 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004830 if (*rest == '\\' && rest[1] != NUL)
4831 ++rest;
4832 *p++ = *rest++;
4833 }
4834 *p++ = NUL;
4835 ++cnt;
4836 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004837
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004838 if (!eap->skip)
4839 {
John Marriottb4ea7712024-08-01 23:01:25 +02004840 size_t kwlen = 0;
4841
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004842 // Adjust flags for use of ":syn include".
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004843 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4844
4845 /*
4846 * 2: Add an entry for each keyword.
4847 */
John Marriottb4ea7712024-08-01 23:01:25 +02004848 for (kw = keyword_copy; --cnt >= 0; kw += kwlen + 1)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004849 {
4850 for (p = vim_strchr(kw, '['); ; )
4851 {
John Marriottb4ea7712024-08-01 23:01:25 +02004852 if (p == NULL)
4853 kwlen = STRLEN(kw);
4854 else
4855 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004856 *p = NUL;
John Marriottb4ea7712024-08-01 23:01:25 +02004857 kwlen = (size_t)(p - kw);
4858 }
4859 add_keyword(kw, kwlen, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004860 syn_opt_arg.cont_in_list,
4861 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004862 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004863 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004864 if (p[1] == NUL)
4865 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00004866 semsg(_(e_error_missing_rsb_str), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004867 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004868 }
4869 if (p[1] == ']')
4870 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004871 if (p[2] != NUL)
4872 {
Bram Moolenaard82a47d2022-01-05 20:24:39 +00004873 semsg(_(e_trailing_char_after_rsb_str_str),
4874 kw, &p[2]);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004875 goto error;
4876 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004877 kw = p + 1; // skip over the "]"
John Marriottb4ea7712024-08-01 23:01:25 +02004878 kwlen = 1;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004879 break;
4880 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004881 if (has_mbyte)
4882 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004883 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004884
4885 mch_memmove(p, p + 1, l);
4886 p += l;
4887 }
4888 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004889 {
4890 p[0] = p[1];
4891 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004892 }
4893 }
4894 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004895 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004896error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004897 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004898 vim_free(syn_opt_arg.cont_in_list);
4899 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004900 }
4901 }
4902
4903 if (rest != NULL)
Bram Moolenaar63b91732021-08-05 20:40:03 +02004904 set_nextcmd(eap, rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004905 else
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00004906 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004907
Bram Moolenaara4d158b2022-08-14 14:17:45 +01004908 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004909 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004910}
4911
4912/*
4913 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4914 *
4915 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4916 */
4917 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004918syn_cmd_match(
4919 exarg_T *eap,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004920 int syncing) // TRUE for ":syntax sync match .. "
Bram Moolenaar071d4272004-06-13 20:20:40 +00004921{
4922 char_u *arg = eap->arg;
4923 char_u *group_name_end;
4924 char_u *rest;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004925 synpat_T item; // the item found in the line
Bram Moolenaar071d4272004-06-13 20:20:40 +00004926 int syn_id;
4927 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004928 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004929 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004930 int conceal_char = NUL;
Bram Moolenaar1966c242020-04-20 22:42:32 +02004931 int orig_called_emsg = called_emsg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004932
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004933 // Isolate the group name, check for validity
Bram Moolenaar071d4272004-06-13 20:20:40 +00004934 rest = get_group_name(arg, &group_name_end);
4935
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004936 // Get options before the pattern
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004937 syn_opt_arg.flags = 0;
4938 syn_opt_arg.keyword = FALSE;
4939 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4940 syn_opt_arg.has_cont_list = TRUE;
4941 syn_opt_arg.cont_list = NULL;
4942 syn_opt_arg.cont_in_list = NULL;
4943 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01004944 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004945
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004946 // get the pattern.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004947 init_syn_patterns();
Bram Moolenaara80faa82020-04-12 19:37:17 +02004948 CLEAR_FIELD(item);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004949 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004950 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4951 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004952
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004953 // Get options after the pattern
Bram Moolenaarde318c52017-01-17 16:27:10 +01004954 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004955
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004956 if (rest != NULL) // all arguments are valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00004957 {
4958 /*
4959 * Check for trailing command and illegal trailing arguments.
4960 */
Bram Moolenaar63b91732021-08-05 20:40:03 +02004961 set_nextcmd(eap, rest);
Bram Moolenaar1966c242020-04-20 22:42:32 +02004962 if (!ends_excmd2(eap->cmd, rest) || eap->skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004963 rest = NULL;
Yegappan Lakshmananfadc02a2023-01-27 21:03:12 +00004964 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) == OK
Bram Moolenaar071d4272004-06-13 20:20:40 +00004965 && (syn_id = syn_check_group(arg,
4966 (int)(group_name_end - arg))) != 0)
4967 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004968 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004969 /*
4970 * Store the pattern in the syn_items list
4971 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004972 idx = curwin->w_s->b_syn_patterns.ga_len;
4973 SYN_ITEMS(curwin->w_s)[idx] = item;
4974 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4975 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4976 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4977 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4978 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4979 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4980 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4981 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004982 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004983#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02004984 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004985#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004986 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004987 curwin->w_s->b_syn_containedin = TRUE;
4988 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4989 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004990
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01004991 // remember that we found a match for syncing on
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004992 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004993 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004994#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004995 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004996 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004997#endif
4998
Bram Moolenaara4d158b2022-08-14 14:17:45 +01004999 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005000 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
5001 return; // don't free the progs and patterns now
Bram Moolenaar071d4272004-06-13 20:20:40 +00005002 }
5003 }
5004
5005 /*
5006 * Something failed, free the allocated memory.
5007 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005008 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005009 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005010 vim_free(syn_opt_arg.cont_list);
5011 vim_free(syn_opt_arg.cont_in_list);
5012 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005013
Bram Moolenaar1966c242020-04-20 22:42:32 +02005014 if (rest == NULL && called_emsg == orig_called_emsg)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005015 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005016}
5017
5018/*
5019 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5020 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5021 */
5022 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005023syn_cmd_region(
5024 exarg_T *eap,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005025 int syncing) // TRUE for ":syntax sync region .."
Bram Moolenaar071d4272004-06-13 20:20:40 +00005026{
5027 char_u *arg = eap->arg;
5028 char_u *group_name_end;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005029 char_u *rest; // next arg, NULL on error
Bram Moolenaar071d4272004-06-13 20:20:40 +00005030 char_u *key_end;
5031 char_u *key = NULL;
5032 char_u *p;
5033 int item;
5034#define ITEM_START 0
5035#define ITEM_SKIP 1
5036#define ITEM_END 2
5037#define ITEM_MATCHGROUP 3
5038 struct pat_ptr
5039 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005040 synpat_T *pp_synp; // pointer to syn_pattern
5041 int pp_matchgroup_id; // matchgroup ID
5042 struct pat_ptr *pp_next; // pointer to next pat_ptr
Bram Moolenaar071d4272004-06-13 20:20:40 +00005043 } *(pat_ptrs[3]);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005044 // patterns found in the line
Bram Moolenaar071d4272004-06-13 20:20:40 +00005045 struct pat_ptr *ppp;
5046 struct pat_ptr *ppp_next;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005047 int pat_count = 0; // nr of syn_patterns found
Bram Moolenaar071d4272004-06-13 20:20:40 +00005048 int syn_id;
5049 int matchgroup_id = 0;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005050 int not_enough = FALSE; // not enough arguments
5051 int illegal = FALSE; // illegal arguments
Bram Moolenaar071d4272004-06-13 20:20:40 +00005052 int success = FALSE;
5053 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005054 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005055 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005056
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005057 // Isolate the group name, check for validity
Bram Moolenaar071d4272004-06-13 20:20:40 +00005058 rest = get_group_name(arg, &group_name_end);
5059
5060 pat_ptrs[0] = NULL;
5061 pat_ptrs[1] = NULL;
5062 pat_ptrs[2] = NULL;
5063
5064 init_syn_patterns();
5065
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005066 syn_opt_arg.flags = 0;
5067 syn_opt_arg.keyword = FALSE;
5068 syn_opt_arg.sync_idx = NULL;
5069 syn_opt_arg.has_cont_list = TRUE;
5070 syn_opt_arg.cont_list = NULL;
5071 syn_opt_arg.cont_in_list = NULL;
5072 syn_opt_arg.next_list = NULL;
5073
Bram Moolenaar071d4272004-06-13 20:20:40 +00005074 /*
5075 * get the options, patterns and matchgroup.
5076 */
Bram Moolenaar1966c242020-04-20 22:42:32 +02005077 while (rest != NULL && !ends_excmd2(eap->cmd, rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005078 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005079 // Check for option arguments
Bram Moolenaarde318c52017-01-17 16:27:10 +01005080 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar1966c242020-04-20 22:42:32 +02005081 if (rest == NULL || ends_excmd2(eap->cmd, rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005082 break;
5083
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005084 // must be a pattern or matchgroup then
Bram Moolenaar071d4272004-06-13 20:20:40 +00005085 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005086 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005087 ++key_end;
5088 vim_free(key);
Bram Moolenaardf44a272020-06-07 20:49:05 +02005089 key = vim_strnsave_up(rest, key_end - rest);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005090 if (key == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00005091 {
5092 rest = NULL;
5093 break;
5094 }
5095 if (STRCMP(key, "MATCHGROUP") == 0)
5096 item = ITEM_MATCHGROUP;
5097 else if (STRCMP(key, "START") == 0)
5098 item = ITEM_START;
5099 else if (STRCMP(key, "END") == 0)
5100 item = ITEM_END;
5101 else if (STRCMP(key, "SKIP") == 0)
5102 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005103 if (pat_ptrs[ITEM_SKIP] != NULL) // one skip pattern allowed
Bram Moolenaar071d4272004-06-13 20:20:40 +00005104 {
5105 illegal = TRUE;
5106 break;
5107 }
5108 item = ITEM_SKIP;
5109 }
5110 else
5111 break;
5112 rest = skipwhite(key_end);
5113 if (*rest != '=')
5114 {
5115 rest = NULL;
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005116 semsg(_(e_missing_equal_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005117 break;
5118 }
5119 rest = skipwhite(rest + 1);
5120 if (*rest == NUL)
5121 {
5122 not_enough = TRUE;
5123 break;
5124 }
5125
5126 if (item == ITEM_MATCHGROUP)
5127 {
5128 p = skiptowhite(rest);
5129 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5130 matchgroup_id = 0;
5131 else
5132 {
5133 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5134 if (matchgroup_id == 0)
5135 {
5136 illegal = TRUE;
5137 break;
5138 }
5139 }
5140 rest = skipwhite(p);
5141 }
5142 else
5143 {
5144 /*
5145 * Allocate room for a syn_pattern, and link it in the list of
5146 * syn_patterns for this item, at the start (because the list is
5147 * used from end to start).
5148 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005149 ppp = ALLOC_ONE(struct pat_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005150 if (ppp == NULL)
5151 {
5152 rest = NULL;
5153 break;
5154 }
5155 ppp->pp_next = pat_ptrs[item];
5156 pat_ptrs[item] = ppp;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005157 ppp->pp_synp = ALLOC_CLEAR_ONE(synpat_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005158 if (ppp->pp_synp == NULL)
5159 {
5160 rest = NULL;
5161 break;
5162 }
5163
5164 /*
5165 * Get the syntax pattern and the following offset(s).
5166 */
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005167 // Enable the appropriate \z specials.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005168 if (item == ITEM_START)
5169 reg_do_extmatch = REX_SET;
5170 else if (item == ITEM_SKIP || item == ITEM_END)
5171 reg_do_extmatch = REX_USE;
5172 rest = get_syn_pattern(rest, ppp->pp_synp);
5173 reg_do_extmatch = 0;
5174 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005175 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005176 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5177 ppp->pp_matchgroup_id = matchgroup_id;
5178 ++pat_count;
5179 }
5180 }
5181 vim_free(key);
5182 if (illegal || not_enough)
5183 rest = NULL;
5184
5185 /*
5186 * Must have a "start" and "end" pattern.
5187 */
5188 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5189 pat_ptrs[ITEM_END] == NULL))
5190 {
5191 not_enough = TRUE;
5192 rest = NULL;
5193 }
5194
5195 if (rest != NULL)
5196 {
5197 /*
5198 * Check for trailing garbage or command.
5199 * If OK, add the item.
5200 */
Bram Moolenaar63b91732021-08-05 20:40:03 +02005201 set_nextcmd(eap, rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005202 if (!ends_excmd(*rest) || eap->skip)
5203 rest = NULL;
Yegappan Lakshmananfadc02a2023-01-27 21:03:12 +00005204 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) == OK
Bram Moolenaar071d4272004-06-13 20:20:40 +00005205 && (syn_id = syn_check_group(arg,
5206 (int)(group_name_end - arg))) != 0)
5207 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005208 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005209 /*
5210 * Store the start/skip/end in the syn_items list
5211 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005212 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005213 for (item = ITEM_START; item <= ITEM_END; ++item)
5214 {
5215 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5216 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005217 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5218 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5219 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005220 (item == ITEM_START) ? SPTYPE_START :
5221 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005222 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5223 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005224 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5225 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005226 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005227 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005228#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005229 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005230#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005231 if (item == ITEM_START)
5232 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005233 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005234 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005235 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005236 syn_opt_arg.cont_in_list;
5237 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005238 curwin->w_s->b_syn_containedin = TRUE;
5239 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005240 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005241 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005242 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005243 ++idx;
5244#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005245 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005246 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005247#endif
5248 }
5249 }
5250
Bram Moolenaara4d158b2022-08-14 14:17:45 +01005251 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005252 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
5253 success = TRUE; // don't free the progs and patterns now
Bram Moolenaar071d4272004-06-13 20:20:40 +00005254 }
5255 }
5256
5257 /*
5258 * Free the allocated memory.
5259 */
5260 for (item = ITEM_START; item <= ITEM_END; ++item)
5261 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5262 {
Bram Moolenaar4bbfb0f2019-08-31 15:28:02 +02005263 if (!success && ppp->pp_synp != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005264 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005265 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005266 vim_free(ppp->pp_synp->sp_pattern);
5267 }
5268 vim_free(ppp->pp_synp);
5269 ppp_next = ppp->pp_next;
5270 vim_free(ppp);
5271 }
5272
5273 if (!success)
5274 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005275 vim_free(syn_opt_arg.cont_list);
5276 vim_free(syn_opt_arg.cont_in_list);
5277 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005278 if (not_enough)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005279 semsg(_(e_not_enough_arguments_syntax_region_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005280 else if (illegal || rest == NULL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005281 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005282 }
5283}
5284
5285/*
5286 * A simple syntax group ID comparison function suitable for use in qsort()
5287 */
5288 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005289syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005290{
5291 const short *s1 = v1;
5292 const short *s2 = v2;
5293
5294 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5295}
5296
5297/*
5298 * Combines lists of syntax clusters.
5299 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5300 */
5301 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005302syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005303{
5304 int count1 = 0;
5305 int count2 = 0;
5306 short *g1;
5307 short *g2;
5308 short *clstr = NULL;
5309 int count;
5310 int round;
5311
5312 /*
5313 * Handle degenerate cases.
5314 */
5315 if (*clstr2 == NULL)
5316 return;
5317 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5318 {
5319 if (list_op == CLUSTER_REPLACE)
5320 vim_free(*clstr1);
5321 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5322 *clstr1 = *clstr2;
5323 else
5324 vim_free(*clstr2);
5325 return;
5326 }
5327
5328 for (g1 = *clstr1; *g1; g1++)
5329 ++count1;
5330 for (g2 = *clstr2; *g2; g2++)
5331 ++count2;
5332
5333 /*
5334 * For speed purposes, sort both lists.
5335 */
5336 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5337 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5338
5339 /*
5340 * We proceed in two passes; in round 1, we count the elements to place
5341 * in the new list, and in round 2, we allocate and populate the new
5342 * list. For speed, we use a mergesort-like method, adding the smaller
5343 * of the current elements in each list to the new list.
5344 */
5345 for (round = 1; round <= 2; round++)
5346 {
5347 g1 = *clstr1;
5348 g2 = *clstr2;
5349 count = 0;
5350
5351 /*
5352 * First, loop through the lists until one of them is empty.
5353 */
5354 while (*g1 && *g2)
5355 {
5356 /*
5357 * We always want to add from the first list.
5358 */
5359 if (*g1 < *g2)
5360 {
5361 if (round == 2)
5362 clstr[count] = *g1;
5363 count++;
5364 g1++;
5365 continue;
5366 }
5367 /*
5368 * We only want to add from the second list if we're adding the
5369 * lists.
5370 */
5371 if (list_op == CLUSTER_ADD)
5372 {
5373 if (round == 2)
5374 clstr[count] = *g2;
5375 count++;
5376 }
5377 if (*g1 == *g2)
5378 g1++;
5379 g2++;
5380 }
5381
5382 /*
5383 * Now add the leftovers from whichever list didn't get finished
5384 * first. As before, we only want to add from the second list if
5385 * we're adding the lists.
5386 */
5387 for (; *g1; g1++, count++)
5388 if (round == 2)
5389 clstr[count] = *g1;
5390 if (list_op == CLUSTER_ADD)
5391 for (; *g2; g2++, count++)
5392 if (round == 2)
5393 clstr[count] = *g2;
5394
5395 if (round == 1)
5396 {
5397 /*
5398 * If the group ended up empty, we don't need to allocate any
5399 * space for it.
5400 */
5401 if (count == 0)
5402 {
5403 clstr = NULL;
5404 break;
5405 }
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005406 clstr = ALLOC_MULT(short, count + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005407 if (clstr == NULL)
5408 break;
5409 clstr[count] = 0;
5410 }
5411 }
5412
5413 /*
5414 * Finally, put the new list in place.
5415 */
5416 vim_free(*clstr1);
5417 vim_free(*clstr2);
5418 *clstr1 = clstr;
5419}
5420
5421/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005422 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005423 * If it is not found, 0 is returned.
5424 */
5425 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005426syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005427{
5428 int i;
5429 char_u *name_u;
5430
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005431 // Avoid using stricmp() too much, it's slow on some systems
Bram Moolenaar071d4272004-06-13 20:20:40 +00005432 name_u = vim_strsave_up(name);
5433 if (name_u == NULL)
5434 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005435 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5436 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5437 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005438 break;
5439 vim_free(name_u);
5440 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5441}
5442
5443/*
5444 * Like syn_scl_name2id(), but take a pointer + length argument.
5445 */
5446 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005447syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005448{
5449 char_u *name;
5450 int id = 0;
5451
5452 name = vim_strnsave(linep, len);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00005453 if (name == NULL)
5454 return 0;
5455
5456 id = syn_scl_name2id(name);
5457 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005458 return id;
5459}
5460
5461/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005462 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005463 * The argument is a pointer to the name and the length of the name.
5464 * If it doesn't exist yet, a new entry is created.
5465 * Return 0 for failure.
5466 */
5467 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005468syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005469{
5470 int id;
5471 char_u *name;
5472
5473 name = vim_strnsave(pp, len);
5474 if (name == NULL)
5475 return 0;
5476
5477 id = syn_scl_name2id(name);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005478 if (id == 0) // doesn't exist yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00005479 id = syn_add_cluster(name);
5480 else
5481 vim_free(name);
5482 return id;
5483}
5484
5485/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005486 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005487 * "name" must be an allocated string, it will be consumed.
5488 * Return 0 for failure.
5489 */
5490 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005491syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005492{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005493 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005494
5495 /*
5496 * First call for this growarray: init growing array.
5497 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005498 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005499 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005500 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5501 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005502 }
5503
Bram Moolenaar42431a72011-04-01 14:44:59 +02005504 len = curwin->w_s->b_syn_clusters.ga_len;
5505 if (len >= MAX_CLUSTER_ID)
5506 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00005507 emsg(_(e_too_many_syntax_clusters));
Bram Moolenaar42431a72011-04-01 14:44:59 +02005508 vim_free(name);
5509 return 0;
5510 }
5511
Bram Moolenaar071d4272004-06-13 20:20:40 +00005512 /*
5513 * Make room for at least one other cluster entry.
5514 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005515 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005516 {
5517 vim_free(name);
5518 return 0;
5519 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005520
Bram Moolenaara80faa82020-04-12 19:37:17 +02005521 CLEAR_POINTER(&(SYN_CLSTR(curwin->w_s)[len]));
Bram Moolenaar860cae12010-06-05 23:22:07 +02005522 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5523 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5524 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5525 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005526
Bram Moolenaar217ad922005-03-20 22:37:15 +00005527 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005528 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005529 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005530 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005531
Bram Moolenaar071d4272004-06-13 20:20:40 +00005532 return len + SYNID_CLUSTER;
5533}
5534
5535/*
5536 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5537 * [add={groupname},..] [remove={groupname},..]".
5538 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005539 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005540syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005541{
5542 char_u *arg = eap->arg;
5543 char_u *group_name_end;
5544 char_u *rest;
5545 int scl_id;
5546 short *clstr_list;
5547 int got_clstr = FALSE;
5548 int opt_len;
5549 int list_op;
5550
5551 eap->nextcmd = find_nextcmd(arg);
5552 if (eap->skip)
5553 return;
5554
5555 rest = get_group_name(arg, &group_name_end);
5556
5557 if (rest != NULL)
5558 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005559 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5560 if (scl_id == 0)
5561 return;
5562 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005563
5564 for (;;)
5565 {
5566 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005567 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005568 {
5569 opt_len = 3;
5570 list_op = CLUSTER_ADD;
5571 }
5572 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005573 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005574 {
5575 opt_len = 6;
5576 list_op = CLUSTER_SUBTRACT;
5577 }
5578 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005579 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005580 {
5581 opt_len = 8;
5582 list_op = CLUSTER_REPLACE;
5583 }
5584 else
5585 break;
5586
5587 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005588 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005589 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005590 semsg(_(e_invalid_argument_str), rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005591 break;
5592 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005593 if (scl_id >= 0)
5594 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005595 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005596 else
5597 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005598 got_clstr = TRUE;
5599 }
5600
5601 if (got_clstr)
5602 {
Bram Moolenaara4d158b2022-08-14 14:17:45 +01005603 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005604 syn_stack_free_all(curwin->w_s); // Need to recompute all.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005605 }
5606 }
5607
5608 if (!got_clstr)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005609 emsg(_(e_no_cluster_specified));
Bram Moolenaar1966c242020-04-20 22:42:32 +02005610 if (rest == NULL || !ends_excmd2(eap->cmd, rest))
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00005611 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005612}
5613
5614/*
5615 * On first call for current buffer: Init growing array.
5616 */
5617 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005618init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005619{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005620 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5621 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005622}
5623
5624/*
5625 * Get one pattern for a ":syntax match" or ":syntax region" command.
5626 * Stores the pattern and program in a synpat_T.
5627 * Returns a pointer to the next argument, or NULL in case of an error.
5628 */
5629 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005630get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005631{
5632 char_u *end;
5633 int *p;
5634 int idx;
5635 char_u *cpo_save;
5636
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005637 // need at least three chars
Bram Moolenaar38219782015-08-11 15:27:13 +02005638 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005639 return NULL;
5640
Bram Moolenaare8c4abb2020-04-02 21:13:25 +02005641 end = skip_regexp(arg + 1, *arg, TRUE);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005642 if (*end != *arg) // end delimiter not found
Bram Moolenaar071d4272004-06-13 20:20:40 +00005643 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005644 semsg(_(e_pattern_delimiter_not_found_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005645 return NULL;
5646 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005647 // store the pattern and compiled regexp program
Bram Moolenaar71ccd032020-06-12 22:59:11 +02005648 if ((ci->sp_pattern = vim_strnsave(arg + 1, end - arg - 1)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005649 return NULL;
5650
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005651 // Make 'cpoptions' empty, to avoid the 'l' flag
Bram Moolenaar071d4272004-06-13 20:20:40 +00005652 cpo_save = p_cpo;
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01005653 p_cpo = empty_option;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005654 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5655 p_cpo = cpo_save;
5656
5657 if (ci->sp_prog == NULL)
5658 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005659 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005660#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005661 syn_clear_time(&ci->sp_time);
5662#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005663
5664 /*
5665 * Check for a match, highlight or region offset.
5666 */
5667 ++end;
5668 do
5669 {
5670 for (idx = SPO_COUNT; --idx >= 0; )
5671 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5672 break;
5673 if (idx >= 0)
5674 {
5675 p = &(ci->sp_offsets[idx]);
5676 if (idx != SPO_LC_OFF)
5677 switch (end[3])
5678 {
5679 case 's': break;
5680 case 'b': break;
5681 case 'e': idx += SPO_COUNT; break;
5682 default: idx = -1; break;
5683 }
5684 if (idx >= 0)
5685 {
5686 ci->sp_off_flags |= (1 << idx);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005687 if (idx == SPO_LC_OFF) // lc=99
Bram Moolenaar071d4272004-06-13 20:20:40 +00005688 {
5689 end += 3;
5690 *p = getdigits(&end);
5691
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005692 // "lc=" offset automatically sets "ms=" offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00005693 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5694 {
5695 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5696 ci->sp_offsets[SPO_MS_OFF] = *p;
5697 }
5698 }
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005699 else // yy=x+99
Bram Moolenaar071d4272004-06-13 20:20:40 +00005700 {
5701 end += 4;
5702 if (*end == '+')
5703 {
5704 ++end;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005705 *p = getdigits(&end); // positive offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00005706 }
5707 else if (*end == '-')
5708 {
5709 ++end;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005710 *p = -getdigits(&end); // negative offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00005711 }
5712 }
5713 if (*end != ',')
5714 break;
5715 ++end;
5716 }
5717 }
5718 } while (idx >= 0);
5719
Bram Moolenaar1966c242020-04-20 22:42:32 +02005720 if (!ends_excmd2(arg, end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005721 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005722 semsg(_(e_garbage_after_pattern_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005723 return NULL;
5724 }
5725 return skipwhite(end);
5726}
5727
5728/*
5729 * Handle ":syntax sync .." command.
5730 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005731 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005732syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005733{
5734 char_u *arg_start = eap->arg;
5735 char_u *arg_end;
5736 char_u *key = NULL;
5737 char_u *next_arg;
5738 int illegal = FALSE;
5739 int finished = FALSE;
5740 long n;
5741 char_u *cpo_save;
5742
Bram Moolenaar1966c242020-04-20 22:42:32 +02005743 if (ends_excmd2(eap->cmd, arg_start))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005744 {
5745 syn_cmd_list(eap, TRUE);
5746 return;
5747 }
5748
Bram Moolenaar1966c242020-04-20 22:42:32 +02005749 while (!ends_excmd2(eap->cmd, arg_start))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005750 {
5751 arg_end = skiptowhite(arg_start);
5752 next_arg = skipwhite(arg_end);
5753 vim_free(key);
Bram Moolenaardf44a272020-06-07 20:49:05 +02005754 key = vim_strnsave_up(arg_start, arg_end - arg_start);
Bram Moolenaar58bb61c2020-07-10 20:30:12 +02005755 if (key == NULL)
5756 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005757 if (STRCMP(key, "CCOMMENT") == 0)
5758 {
5759 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005760 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar1966c242020-04-20 22:42:32 +02005761 if (!ends_excmd2(eap->cmd, next_arg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005762 {
5763 arg_end = skiptowhite(next_arg);
5764 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005765 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005766 (int)(arg_end - next_arg));
5767 next_arg = skipwhite(arg_end);
5768 }
5769 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005770 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005771 }
5772 else if ( STRNCMP(key, "LINES", 5) == 0
5773 || STRNCMP(key, "MINLINES", 8) == 0
5774 || STRNCMP(key, "MAXLINES", 8) == 0
5775 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5776 {
5777 if (key[4] == 'S')
5778 arg_end = key + 6;
5779 else if (key[0] == 'L')
5780 arg_end = key + 11;
5781 else
5782 arg_end = key + 9;
5783 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5784 {
5785 illegal = TRUE;
5786 break;
5787 }
5788 n = getdigits(&arg_end);
5789 if (!eap->skip)
5790 {
5791 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005792 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005793 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005794 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005795 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005796 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005797 }
5798 }
5799 else if (STRCMP(key, "FROMSTART") == 0)
5800 {
5801 if (!eap->skip)
5802 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005803 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5804 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005805 }
5806 }
5807 else if (STRCMP(key, "LINECONT") == 0)
5808 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005809 if (*next_arg == NUL) // missing pattern
Bram Moolenaar2795e212016-01-05 22:04:49 +01005810 {
5811 illegal = TRUE;
5812 break;
5813 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005814 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005815 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005816 emsg(_(e_syntax_sync_line_continuations_pattern_specified_twice));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005817 finished = TRUE;
5818 break;
5819 }
Bram Moolenaare8c4abb2020-04-02 21:13:25 +02005820 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005821 if (*arg_end != *next_arg) // end delimiter not found
Bram Moolenaar071d4272004-06-13 20:20:40 +00005822 {
5823 illegal = TRUE;
5824 break;
5825 }
5826
5827 if (!eap->skip)
5828 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005829 // store the pattern and compiled regexp program
Bram Moolenaar71ccd032020-06-12 22:59:11 +02005830 if ((curwin->w_s->b_syn_linecont_pat =
5831 vim_strnsave(next_arg + 1,
5832 arg_end - next_arg - 1)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005833 {
5834 finished = TRUE;
5835 break;
5836 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005837 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005838
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005839 // Make 'cpoptions' empty, to avoid the 'l' flag
Bram Moolenaar071d4272004-06-13 20:20:40 +00005840 cpo_save = p_cpo;
Bram Moolenaare5a2dc82021-01-03 19:52:05 +01005841 p_cpo = empty_option;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005842 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005843 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005844 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005845#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005846 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5847#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005848
Bram Moolenaar860cae12010-06-05 23:22:07 +02005849 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005850 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01005851 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005852 finished = TRUE;
5853 break;
5854 }
5855 }
5856 next_arg = skipwhite(arg_end + 1);
5857 }
5858 else
5859 {
5860 eap->arg = next_arg;
5861 if (STRCMP(key, "MATCH") == 0)
5862 syn_cmd_match(eap, TRUE);
5863 else if (STRCMP(key, "REGION") == 0)
5864 syn_cmd_region(eap, TRUE);
5865 else if (STRCMP(key, "CLEAR") == 0)
5866 syn_cmd_clear(eap, TRUE);
5867 else
5868 illegal = TRUE;
5869 finished = TRUE;
5870 break;
5871 }
5872 arg_start = next_arg;
5873 }
5874 vim_free(key);
5875 if (illegal)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005876 semsg(_(e_illegal_arguments_str), arg_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005877 else if (!finished)
5878 {
Bram Moolenaar63b91732021-08-05 20:40:03 +02005879 set_nextcmd(eap, arg_start);
Bram Moolenaara4d158b2022-08-14 14:17:45 +01005880 redraw_curbuf_later(UPD_SOME_VALID);
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005881 syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005882 }
5883}
5884
5885/*
5886 * Convert a line of highlight group names into a list of group ID numbers.
5887 * "arg" should point to the "contains" or "nextgroup" keyword.
5888 * "arg" is advanced to after the last group name.
5889 * Careful: the argument is modified (NULs added).
5890 * returns FAIL for some error, OK for success.
5891 */
5892 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005893get_id_list(
5894 char_u **arg,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005895 int keylen, // length of keyword
5896 short **list, // where to store the resulting list, if not
5897 // NULL, the list is silently skipped!
Bram Moolenaarde318c52017-01-17 16:27:10 +01005898 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005899{
5900 char_u *p = NULL;
5901 char_u *end;
5902 int round;
5903 int count;
5904 int total_count = 0;
5905 short *retval = NULL;
5906 char_u *name;
5907 regmatch_T regmatch;
5908 int id;
5909 int i;
5910 int failed = FALSE;
5911
5912 /*
5913 * We parse the list twice:
5914 * round == 1: count the number of items, allocate the array.
5915 * round == 2: fill the array with the items.
5916 * In round 1 new groups may be added, causing the number of items to
5917 * grow when a regexp is used. In that case round 1 is done once again.
5918 */
5919 for (round = 1; round <= 2; ++round)
5920 {
5921 /*
5922 * skip "contains"
5923 */
5924 p = skipwhite(*arg + keylen);
5925 if (*p != '=')
5926 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005927 semsg(_(e_missing_equal_sign_str), *arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005928 break;
5929 }
5930 p = skipwhite(p + 1);
Bram Moolenaar1966c242020-04-20 22:42:32 +02005931 if (ends_excmd2(*arg, p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005932 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005933 semsg(_(e_empty_argument_str), *arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005934 break;
5935 }
5936
5937 /*
5938 * parse the arguments after "contains"
5939 */
5940 count = 0;
Bram Moolenaar1966c242020-04-20 22:42:32 +02005941 while (!ends_excmd2(*arg, p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005942 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01005943 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005944 ;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01005945 name = alloc(end - p + 3); // leave room for "^$"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005946 if (name == NULL)
5947 {
5948 failed = TRUE;
5949 break;
5950 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005951 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005952 if ( STRCMP(name + 1, "ALLBUT") == 0
5953 || STRCMP(name + 1, "ALL") == 0
5954 || STRCMP(name + 1, "TOP") == 0
5955 || STRCMP(name + 1, "CONTAINED") == 0)
5956 {
5957 if (TOUPPER_ASC(**arg) != 'C')
5958 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005959 semsg(_(e_str_not_allowed_here), name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005960 failed = TRUE;
5961 vim_free(name);
5962 break;
5963 }
5964 if (count != 0)
5965 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005966 semsg(_(e_str_must_be_first_in_contains_list), name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005967 failed = TRUE;
5968 vim_free(name);
5969 break;
5970 }
5971 if (name[1] == 'A')
Theodore Duboisf50d5362025-02-05 23:59:25 +01005972 id = SYNID_ALLBUT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005973 else if (name[1] == 'T')
Theodore Duboisf50d5362025-02-05 23:59:25 +01005974 id = SYNID_TOP;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005975 else
Theodore Duboisf50d5362025-02-05 23:59:25 +01005976 id = SYNID_CONTAINED;
5977 id += current_syn_inc_tag;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005978 }
5979 else if (name[1] == '@')
5980 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01005981 if (skip)
5982 id = -1;
5983 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01005984 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005985 }
5986 else
5987 {
5988 /*
5989 * Handle full group name.
5990 */
5991 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5992 id = syn_check_group(name + 1, (int)(end - p));
5993 else
5994 {
5995 /*
5996 * Handle match of regexp with group names.
5997 */
5998 *name = '^';
5999 STRCAT(name, "$");
6000 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6001 if (regmatch.regprog == NULL)
6002 {
6003 failed = TRUE;
6004 vim_free(name);
6005 break;
6006 }
6007
6008 regmatch.rm_ic = TRUE;
6009 id = 0;
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02006010 for (i = highlight_num_groups(); --i >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006011 {
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02006012 if (vim_regexec(&regmatch, highlight_group_name(i),
Bram Moolenaar071d4272004-06-13 20:20:40 +00006013 (colnr_T)0))
6014 {
6015 if (round == 2)
6016 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006017 // Got more items than expected; can happen
6018 // when adding items that match:
6019 // "contains=a.*b,axb".
6020 // Go back to first round
Bram Moolenaar071d4272004-06-13 20:20:40 +00006021 if (count >= total_count)
6022 {
6023 vim_free(retval);
6024 round = 1;
6025 }
6026 else
6027 retval[count] = i + 1;
6028 }
6029 ++count;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006030 id = -1; // remember that we found one
Bram Moolenaar071d4272004-06-13 20:20:40 +00006031 }
6032 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006033 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006034 }
6035 }
6036 vim_free(name);
6037 if (id == 0)
6038 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00006039 semsg(_(e_unknown_group_name_str), p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006040 failed = TRUE;
6041 break;
6042 }
6043 if (id > 0)
6044 {
6045 if (round == 2)
6046 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006047 // Got more items than expected, go back to first round
Bram Moolenaar071d4272004-06-13 20:20:40 +00006048 if (count >= total_count)
6049 {
6050 vim_free(retval);
6051 round = 1;
6052 }
6053 else
6054 retval[count] = id;
6055 }
6056 ++count;
6057 }
6058 p = skipwhite(end);
6059 if (*p != ',')
6060 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006061 p = skipwhite(p + 1); // skip comma in between arguments
Bram Moolenaar071d4272004-06-13 20:20:40 +00006062 }
6063 if (failed)
6064 break;
6065 if (round == 1)
6066 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006067 retval = ALLOC_MULT(short, count + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006068 if (retval == NULL)
6069 break;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006070 retval[count] = 0; // zero means end of the list
Bram Moolenaar071d4272004-06-13 20:20:40 +00006071 total_count = count;
6072 }
6073 }
6074
6075 *arg = p;
6076 if (failed || retval == NULL)
6077 {
6078 vim_free(retval);
6079 return FAIL;
6080 }
6081
6082 if (*list == NULL)
6083 *list = retval;
6084 else
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006085 vim_free(retval); // list already found, don't overwrite it
Bram Moolenaar071d4272004-06-13 20:20:40 +00006086
6087 return OK;
6088}
6089
6090/*
6091 * Make a copy of an ID list.
6092 */
6093 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006094copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006095{
6096 int len;
6097 int count;
6098 short *retval;
6099
6100 if (list == NULL)
6101 return NULL;
6102
6103 for (count = 0; list[count]; ++count)
6104 ;
6105 len = (count + 1) * sizeof(short);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006106 retval = alloc(len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006107 if (retval != NULL)
6108 mch_memmove(retval, list, (size_t)len);
6109
6110 return retval;
6111}
6112
6113/*
6114 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6115 * "cur_si" can be NULL if not checking the "containedin" list.
6116 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6117 * the current item.
6118 * This function is called very often, keep it fast!!
6119 */
6120 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006121in_id_list(
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006122 stateitem_T *cur_si, // current item or NULL
6123 short *list, // id list
6124 struct sp_syn *ssp, // group id and ":syn include" tag of group
Theodore Duboisf50d5362025-02-05 23:59:25 +01006125 int flags) // group flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00006126{
6127 int retval;
6128 short *scl_list;
6129 short item;
6130 short id = ssp->id;
6131 static int depth = 0;
6132 int r;
Theodore Duboisf50d5362025-02-05 23:59:25 +01006133 int toplevel;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006134
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006135 // If ssp has a "containedin" list and "cur_si" is in it, return TRUE.
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006136 if (cur_si != NULL && ssp->cont_in_list != NULL
6137 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006138 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006139 // Ignore transparent items without a contains argument. Double check
6140 // that we don't go back past the first one.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006141 while ((cur_si->si_flags & HL_TRANS_CONT)
6142 && cur_si > (stateitem_T *)(current_state.ga_data))
6143 --cur_si;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006144 // cur_si->si_idx is -1 for keywords, these never contain anything.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006145 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006146 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
Theodore Duboisf50d5362025-02-05 23:59:25 +01006147 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006148 return TRUE;
6149 }
6150
6151 if (list == NULL)
6152 return FALSE;
6153
6154 /*
6155 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6156 * inside anything. Only allow not-contained groups.
6157 */
6158 if (list == ID_LIST_ALL)
Theodore Duboisf50d5362025-02-05 23:59:25 +01006159 return !(flags & HL_CONTAINED);
6160
6161 /*
6162 * Is this top-level (i.e. not 'contained') in the file it was declared in?
6163 * For included files, this is different from HL_CONTAINED, which is set
6164 * unconditionally.
6165 */
6166 toplevel = !(flags & HL_CONTAINED) || (flags & HL_INCLUDED_TOPLEVEL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006167
6168 /*
6169 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6170 * contains list. We also require that "id" is at the same ":syn include"
6171 * level as the list.
6172 */
6173 item = *list;
6174 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6175 {
6176 if (item < SYNID_TOP)
6177 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006178 // ALL or ALLBUT: accept all groups in the same file
Bram Moolenaar071d4272004-06-13 20:20:40 +00006179 if (item - SYNID_ALLBUT != ssp->inc_tag)
6180 return FALSE;
6181 }
6182 else if (item < SYNID_CONTAINED)
6183 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006184 // TOP: accept all not-contained groups in the same file
Theodore Duboisf50d5362025-02-05 23:59:25 +01006185 if (item - SYNID_TOP != ssp->inc_tag || !toplevel)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006186 return FALSE;
6187 }
6188 else
6189 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006190 // CONTAINED: accept all contained groups in the same file
Theodore Duboisf50d5362025-02-05 23:59:25 +01006191 if (item - SYNID_CONTAINED != ssp->inc_tag || toplevel)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006192 return FALSE;
6193 }
6194 item = *++list;
6195 retval = FALSE;
6196 }
6197 else
6198 retval = TRUE;
6199
6200 /*
6201 * Return "retval" if id is in the contains list.
6202 */
6203 while (item != 0)
6204 {
6205 if (item == id)
6206 return retval;
6207 if (item >= SYNID_CLUSTER)
6208 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006209 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006210 // restrict recursiveness to 30 to avoid an endless loop for a
6211 // cluster that includes itself (indirectly)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006212 if (scl_list != NULL && depth < 30)
6213 {
6214 ++depth;
Theodore Duboisf50d5362025-02-05 23:59:25 +01006215 r = in_id_list(NULL, scl_list, ssp, flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006216 --depth;
6217 if (r)
6218 return retval;
6219 }
6220 }
6221 item = *++list;
6222 }
6223 return !retval;
6224}
6225
6226struct subcommand
6227{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006228 char *name; // subcommand name
6229 void (*func)(exarg_T *, int); // function to call
Bram Moolenaar071d4272004-06-13 20:20:40 +00006230};
6231
6232static struct subcommand subcommands[] =
6233{
6234 {"case", syn_cmd_case},
6235 {"clear", syn_cmd_clear},
6236 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006237 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006238 {"enable", syn_cmd_enable},
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006239 {"foldlevel", syn_cmd_foldlevel},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006240 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006241 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006242 {"keyword", syn_cmd_keyword},
6243 {"list", syn_cmd_list},
6244 {"manual", syn_cmd_manual},
6245 {"match", syn_cmd_match},
6246 {"on", syn_cmd_on},
6247 {"off", syn_cmd_off},
6248 {"region", syn_cmd_region},
6249 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006250 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006251 {"sync", syn_cmd_sync},
John Marriottb4ea7712024-08-01 23:01:25 +02006252 {"", syn_cmd_list}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006253};
6254
6255/*
6256 * ":syntax".
6257 * This searches the subcommands[] table for the subcommand name, and calls a
6258 * syntax_subcommand() function to do the rest.
6259 */
6260 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006261ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006262{
6263 char_u *arg = eap->arg;
6264 char_u *subcmd_end;
6265 char_u *subcmd_name;
6266 int i;
6267
6268 syn_cmdlinep = eap->cmdlinep;
6269
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006270 // isolate subcommand name
Bram Moolenaar071d4272004-06-13 20:20:40 +00006271 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6272 ;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02006273 subcmd_name = vim_strnsave(arg, subcmd_end - arg);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00006274 if (subcmd_name == NULL)
6275 return;
6276
6277 if (eap->skip) // skip error messages for all subcommands
6278 ++emsg_skip;
John Marriottb4ea7712024-08-01 23:01:25 +02006279 for (i = 0; i < (int)ARRAY_LENGTH(subcommands); ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006280 {
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00006281 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6282 {
6283 eap->arg = skipwhite(subcmd_end);
6284 (subcommands[i].func)(eap, FALSE);
6285 break;
6286 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006287 }
John Marriottb4ea7712024-08-01 23:01:25 +02006288
6289 if (i == (int)ARRAY_LENGTH(subcommands))
6290 semsg(_(e_invalid_syntax_subcommand_str), subcmd_name);
6291
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00006292 vim_free(subcmd_name);
6293 if (eap->skip)
6294 --emsg_skip;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006295}
6296
Bram Moolenaar860cae12010-06-05 23:22:07 +02006297 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006298ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006299{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006300 char_u *old_value;
6301 char_u *new_value;
6302
Bram Moolenaar860cae12010-06-05 23:22:07 +02006303 if (curwin->w_s == &curwin->w_buffer->b_s)
6304 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006305 curwin->w_s = ALLOC_ONE(synblock_T);
Yegappan Lakshmanan960dcbd2023-03-07 17:45:11 +00006306 CLEAR_POINTER(curwin->w_s);
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006307 hash_init(&curwin->w_s->b_keywtab);
6308 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006309#ifdef FEAT_SPELL
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006310 // TODO: keep the spell checking as it was.
6311 curwin->w_p_spell = FALSE; // No spell checking
Bram Moolenaard1f76af2020-09-13 22:37:34 +02006312 // make sure option values are "empty_option" instead of NULL
Bram Moolenaar860cae12010-06-05 23:22:07 +02006313 clear_string_option(&curwin->w_s->b_p_spc);
6314 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006315 clear_string_option(&curwin->w_s->b_p_spl);
Bram Moolenaard1f76af2020-09-13 22:37:34 +02006316 clear_string_option(&curwin->w_s->b_p_spo);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006317#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006318 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006319 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006320
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006321 // save value of b:current_syntax
Bram Moolenaar1950c352010-06-06 15:21:10 +02006322 old_value = get_var_value((char_u *)"b:current_syntax");
6323 if (old_value != NULL)
6324 old_value = vim_strsave(old_value);
6325
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006326 // Apply the "syntax" autocommand event, this finds and loads the syntax
6327 // file.
Bram Moolenaar860cae12010-06-05 23:22:07 +02006328 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006329
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006330 // move value of b:current_syntax to w:current_syntax
Bram Moolenaar1950c352010-06-06 15:21:10 +02006331 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006332 if (new_value != NULL)
6333 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006334
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006335 // restore value of b:current_syntax
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006336 if (old_value == NULL)
6337 do_unlet((char_u *)"b:current_syntax", TRUE);
6338 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006339 {
6340 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6341 vim_free(old_value);
6342 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006343}
6344
6345 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006346syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006347{
6348 return (win->w_s->b_syn_patterns.ga_len != 0
6349 || win->w_s->b_syn_clusters.ga_len != 0
6350 || win->w_s->b_keywtab.ht_used > 0
6351 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006352}
6353
Bram Moolenaar071d4272004-06-13 20:20:40 +00006354
6355static enum
6356{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006357 EXP_SUBCMD, // expand ":syn" sub-commands
6358 EXP_CASE, // expand ":syn case" arguments
6359 EXP_SPELL, // expand ":syn spell" arguments
bfredlaf9a6002022-08-26 21:58:31 +01006360 EXP_SYNC, // expand ":syn sync" arguments
6361 EXP_CLUSTER // expand ":syn list @cluster" arguments
Bram Moolenaar071d4272004-06-13 20:20:40 +00006362} expand_what;
6363
Bram Moolenaar4f688582007-07-24 12:34:30 +00006364/*
6365 * Reset include_link, include_default, include_none to 0.
6366 * Called when we are done expanding.
6367 */
6368 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006369reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006370{
6371 include_link = include_default = include_none = 0;
6372}
6373
6374/*
6375 * Handle command line completion for :match and :echohl command: Add "None"
6376 * as highlight group.
6377 */
6378 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006379set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006380{
6381 xp->xp_context = EXPAND_HIGHLIGHT;
6382 xp->xp_pattern = arg;
6383 include_none = 1;
6384}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006385
6386/*
6387 * Handle command line completion for :syntax command.
6388 */
6389 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006390set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006391{
6392 char_u *p;
6393
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006394 // Default: expand subcommands
Bram Moolenaar071d4272004-06-13 20:20:40 +00006395 xp->xp_context = EXPAND_SYNTAX;
6396 expand_what = EXP_SUBCMD;
6397 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006398 include_link = 0;
6399 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006400
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00006401 if (*arg == NUL)
6402 return;
6403
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006404 // (part of) subcommand already typed
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00006405 p = skiptowhite(arg);
6406 if (*p == NUL)
6407 return;
6408
6409 // past first word
6410 xp->xp_pattern = skipwhite(p);
6411 if (*skiptowhite(xp->xp_pattern) != NUL)
6412 xp->xp_context = EXPAND_NOTHING;
6413 else if (STRNICMP(arg, "case", p - arg) == 0)
6414 expand_what = EXP_CASE;
6415 else if (STRNICMP(arg, "spell", p - arg) == 0)
6416 expand_what = EXP_SPELL;
6417 else if (STRNICMP(arg, "sync", p - arg) == 0)
6418 expand_what = EXP_SYNC;
6419 else if (STRNICMP(arg, "list", p - arg) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006420 {
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00006421 p = skipwhite(p);
6422 if (*p == '@')
6423 expand_what = EXP_CLUSTER;
6424 else
6425 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006426 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +00006427 else if (STRNICMP(arg, "keyword", p - arg) == 0
6428 || STRNICMP(arg, "region", p - arg) == 0
6429 || STRNICMP(arg, "match", p - arg) == 0)
6430 xp->xp_context = EXPAND_HIGHLIGHT;
6431 else
6432 xp->xp_context = EXPAND_NOTHING;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006433}
6434
Bram Moolenaar071d4272004-06-13 20:20:40 +00006435/*
6436 * Function given to ExpandGeneric() to obtain the list syntax names for
6437 * expansion.
6438 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006439 char_u *
Bram Moolenaar5ff595d2022-08-26 22:36:41 +01006440get_syntax_name(expand_T *xp, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006441{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006442 switch (expand_what)
6443 {
6444 case EXP_SUBCMD:
John Marriottb4ea7712024-08-01 23:01:25 +02006445 if (idx < 0 || idx >= (int)ARRAY_LENGTH(subcommands))
6446 return NULL;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006447 return (char_u *)subcommands[idx].name;
6448 case EXP_CASE:
6449 {
6450 static char *case_args[] = {"match", "ignore", NULL};
6451 return (char_u *)case_args[idx];
6452 }
6453 case EXP_SPELL:
6454 {
6455 static char *spell_args[] =
6456 {"toplevel", "notoplevel", "default", NULL};
6457 return (char_u *)spell_args[idx];
6458 }
6459 case EXP_SYNC:
6460 {
6461 static char *sync_args[] =
6462 {"ccomment", "clear", "fromstart",
6463 "linebreaks=", "linecont", "lines=", "match",
6464 "maxlines=", "minlines=", "region", NULL};
6465 return (char_u *)sync_args[idx];
6466 }
bfredlaf9a6002022-08-26 21:58:31 +01006467 case EXP_CLUSTER:
6468 {
6469 if (idx < curwin->w_s->b_syn_clusters.ga_len)
6470 {
Bram Moolenaar5ff595d2022-08-26 22:36:41 +01006471 vim_snprintf((char *)xp->xp_buf, EXPAND_BUF_LEN, "@%s",
bfredlaf9a6002022-08-26 21:58:31 +01006472 SYN_CLSTR(curwin->w_s)[idx].scl_name);
Bram Moolenaar5ff595d2022-08-26 22:36:41 +01006473 return xp->xp_buf;
bfredlaf9a6002022-08-26 21:58:31 +01006474 }
6475 else
6476 return NULL;
6477 }
Bram Moolenaar2d028392017-01-08 18:28:22 +01006478 }
6479 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006480}
6481
Bram Moolenaar071d4272004-06-13 20:20:40 +00006482
Bram Moolenaar071d4272004-06-13 20:20:40 +00006483/*
6484 * Function called for expression evaluation: get syntax ID at file position.
6485 */
6486 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006487syn_get_id(
6488 win_T *wp,
6489 long lnum,
6490 colnr_T col,
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006491 int trans, // remove transparency
6492 int *spellp, // return: can do spell checking
6493 int keep_state) // keep state of char at "col"
Bram Moolenaar071d4272004-06-13 20:20:40 +00006494{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006495 // When the position is not after the current position and in the same
zeertzjqca7e86c2022-04-16 16:49:24 +01006496 // line of the same window with the same buffer, need to restart parsing.
6497 if (wp != syn_win
6498 || wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006499 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006500 || col < current_col)
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006501 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006502 else if (wp->w_buffer == syn_buf
6503 && lnum == current_lnum
6504 && col > current_col)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006505 // next_match may not be correct when moving around, e.g. with the
6506 // "skip" expression in searchpair()
Bram Moolenaar6773a342016-01-19 20:52:44 +01006507 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006508
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006509 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006510
6511 return (trans ? current_trans_id : current_id);
6512}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006513
Bram Moolenaar860cae12010-06-05 23:22:07 +02006514#if defined(FEAT_CONCEAL) || defined(PROTO)
6515/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006516 * Get extra information about the syntax item. Must be called right after
6517 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006518 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006519 * Returns the current flags.
6520 */
6521 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006522get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006523{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006524 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006525 return current_flags;
6526}
6527
6528/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006529 * Return conceal substitution character
6530 */
6531 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006532syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006533{
6534 return current_sub_char;
6535}
6536#endif
6537
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006538#if defined(FEAT_EVAL) || defined(PROTO)
6539/*
6540 * Return the syntax ID at position "i" in the current stack.
6541 * The caller must have called syn_get_id() before to fill the stack.
6542 * Returns -1 when "i" is out of range.
6543 */
6544 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006545syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006546{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006547 if (i >= current_state.ga_len)
6548 {
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006549 // Need to invalidate the state, because we didn't properly finish it
6550 // for the last character, "keep_state" was TRUE.
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006551 invalidate_current_state();
6552 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006553 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006554 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006555 return CUR_STATE(i).si_id;
6556}
6557#endif
6558
Bram Moolenaar071d4272004-06-13 20:20:40 +00006559#if defined(FEAT_FOLDING) || defined(PROTO)
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006560 static int
6561syn_cur_foldlevel(void)
6562{
6563 int level = 0;
6564 int i;
6565
6566 for (i = 0; i < current_state.ga_len; ++i)
6567 if (CUR_STATE(i).si_flags & HL_FOLD)
6568 ++level;
6569 return level;
6570}
6571
Bram Moolenaar071d4272004-06-13 20:20:40 +00006572/*
6573 * Function called to get folding level for line "lnum" in window "wp".
6574 */
6575 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006576syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006577{
6578 int level = 0;
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006579 int low_level;
6580 int cur_level;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006581
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006582 // Return quickly when there are no fold items at all.
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006583 if (wp->w_s->b_syn_folditems != 0
6584 && !wp->w_s->b_syn_error
6585# ifdef SYN_TIME_LIMIT
6586 && !wp->w_s->b_syn_slow
6587# endif
6588 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006589 {
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006590 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006591
Bram Moolenaare35a52a2020-05-31 19:48:53 +02006592 // Start with the fold level at the start of the line.
6593 level = syn_cur_foldlevel();
6594
6595 if (wp->w_s->b_syn_foldlevel == SYNFLD_MINIMUM)
6596 {
6597 // Find the lowest fold level that is followed by a higher one.
6598 cur_level = level;
6599 low_level = cur_level;
6600 while (!current_finished)
6601 {
6602 (void)syn_current_attr(FALSE, FALSE, NULL, FALSE);
6603 cur_level = syn_cur_foldlevel();
6604 if (cur_level < low_level)
6605 low_level = cur_level;
6606 else if (cur_level > low_level)
6607 level = low_level;
6608 ++current_col;
6609 }
6610 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006611 }
6612 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006613 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006614 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006615 if (level < 0)
6616 level = 0;
6617 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006618 return level;
6619}
6620#endif
6621
Bram Moolenaar01615492015-02-03 13:00:38 +01006622#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006623/*
6624 * ":syntime".
6625 */
6626 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006627ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006628{
6629 if (STRCMP(eap->arg, "on") == 0)
6630 syn_time_on = TRUE;
6631 else if (STRCMP(eap->arg, "off") == 0)
6632 syn_time_on = FALSE;
6633 else if (STRCMP(eap->arg, "clear") == 0)
6634 syntime_clear();
6635 else if (STRCMP(eap->arg, "report") == 0)
6636 syntime_report();
6637 else
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00006638 semsg(_(e_invalid_argument_str), eap->arg);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006639}
6640
6641 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006642syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006643{
6644 profile_zero(&st->total);
6645 profile_zero(&st->slowest);
6646 st->count = 0;
6647 st->match = 0;
6648}
6649
6650/*
6651 * Clear the syntax timing for the current buffer.
6652 */
6653 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006654syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006655{
6656 int idx;
6657 synpat_T *spp;
6658
6659 if (!syntax_present(curwin))
6660 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006661 msg(_(msg_no_items));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006662 return;
6663 }
6664 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6665 {
6666 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6667 syn_clear_time(&spp->sp_time);
6668 }
6669}
6670
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006671/*
6672 * Function given to ExpandGeneric() to obtain the possible arguments of the
6673 * ":syntime {on,off,clear,report}" command.
6674 */
6675 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006676get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006677{
6678 switch (idx)
6679 {
6680 case 0: return (char_u *)"on";
6681 case 1: return (char_u *)"off";
6682 case 2: return (char_u *)"clear";
6683 case 3: return (char_u *)"report";
6684 }
6685 return NULL;
6686}
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006687
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006688typedef struct
6689{
6690 proftime_T total;
6691 int count;
6692 int match;
6693 proftime_T slowest;
6694 proftime_T average;
6695 int id;
6696 char_u *pattern;
6697} time_entry_T;
6698
6699 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006700syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006701{
6702 const time_entry_T *s1 = v1;
6703 const time_entry_T *s2 = v2;
6704
6705 return profile_cmp(&s1->total, &s2->total);
6706}
6707
6708/*
6709 * Clear the syntax timing for the current buffer.
6710 */
6711 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006712syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006713{
6714 int idx;
6715 synpat_T *spp;
Bram Moolenaar73e28dc2022-09-17 21:08:33 +01006716# if defined(FEAT_RELTIME)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006717 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006718# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006719 int len;
John Marriottb4ea7712024-08-01 23:01:25 +02006720 int patlen;
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006721 proftime_T total_total;
6722 int total_count = 0;
6723 garray_T ga;
6724 time_entry_T *p;
6725
6726 if (!syntax_present(curwin))
6727 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006728 msg(_(msg_no_items));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006729 return;
6730 }
6731
6732 ga_init2(&ga, sizeof(time_entry_T), 50);
6733 profile_zero(&total_total);
6734 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6735 {
6736 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6737 if (spp->sp_time.count > 0)
6738 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006739 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006740 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6741 p->total = spp->sp_time.total;
6742 profile_add(&total_total, &spp->sp_time.total);
6743 p->count = spp->sp_time.count;
6744 p->match = spp->sp_time.match;
6745 total_count += spp->sp_time.count;
6746 p->slowest = spp->sp_time.slowest;
Bram Moolenaar73e28dc2022-09-17 21:08:33 +01006747# if defined(FEAT_RELTIME)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006748 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6749 p->average = tm;
6750# endif
6751 p->id = spp->sp_syn.id;
6752 p->pattern = spp->sp_pattern;
6753 ++ga.ga_len;
6754 }
6755 }
6756
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006757 // Sort on total time. Skip if there are no items to avoid passing NULL
6758 // pointer to qsort().
Bram Moolenaara2162552017-01-08 17:46:20 +01006759 if (ga.ga_len > 1)
6760 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006761 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006762
Bram Moolenaar32526b32019-01-19 17:43:09 +01006763 msg_puts_title(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6764 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006765 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6766 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006767 p = ((time_entry_T *)ga.ga_data) + idx;
6768
Bram Moolenaar32526b32019-01-19 17:43:09 +01006769 msg_puts(profile_msg(&p->total));
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006770 msg_puts(" "); // make sure there is always a separating space
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006771 msg_advance(13);
6772 msg_outnum(p->count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006773 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006774 msg_advance(20);
6775 msg_outnum(p->match);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006776 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006777 msg_advance(26);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006778 msg_puts(profile_msg(&p->slowest));
6779 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006780 msg_advance(38);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006781 msg_puts(profile_msg(&p->average));
6782 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006783 msg_advance(50);
Bram Moolenaar2ac6e822019-07-15 22:40:22 +02006784 msg_outtrans(highlight_group_name(p->id - 1));
Bram Moolenaar32526b32019-01-19 17:43:09 +01006785 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006786
6787 msg_advance(69);
6788 if (Columns < 80)
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006789 len = 20; // will wrap anyway
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006790 else
6791 len = Columns - 70;
John Marriottb4ea7712024-08-01 23:01:25 +02006792 patlen = (int)STRLEN(p->pattern);
6793 if (len > patlen)
6794 len = patlen;
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006795 msg_outtrans_len(p->pattern, len);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006796 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006797 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006798 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006799 if (!got_int)
6800 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006801 msg_puts("\n");
6802 msg_puts(profile_msg(&total_total));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006803 msg_advance(13);
6804 msg_outnum(total_count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006805 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006806 }
6807}
6808#endif
6809
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01006810#endif // FEAT_SYN_HL