1edf3f97aSBram Moolenaar /* vi:set ts=8 sts=4 sw=4 noet:
2071d4279SBram Moolenaar *
3071d4279SBram Moolenaar * VIM - Vi IMproved by Bram Moolenaar
4071d4279SBram Moolenaar *
5071d4279SBram Moolenaar * Do ":help uganda" in Vim to read copying and usage conditions.
6071d4279SBram Moolenaar * Do ":help credits" in Vim to see a list of people who contributed.
7071d4279SBram Moolenaar * See README.txt for an overview of the Vim source code.
8071d4279SBram Moolenaar */
9071d4279SBram Moolenaar
10071d4279SBram Moolenaar /*
11071d4279SBram Moolenaar * syntax.c: code for syntax highlighting
12071d4279SBram Moolenaar */
13071d4279SBram Moolenaar
14071d4279SBram Moolenaar #include "vim.h"
15071d4279SBram Moolenaar
16071d4279SBram Moolenaar #if defined(FEAT_SYN_HL) || defined(PROTO)
17071d4279SBram Moolenaar
180d6f5d97SBram Moolenaar #define SYN_NAMELEN 50 // maximum length of a syntax name
19071d4279SBram Moolenaar
200d6f5d97SBram Moolenaar // different types of offsets that are possible
210d6f5d97SBram Moolenaar #define SPO_MS_OFF 0 // match start offset
220d6f5d97SBram Moolenaar #define SPO_ME_OFF 1 // match end offset
230d6f5d97SBram Moolenaar #define SPO_HS_OFF 2 // highl. start offset
240d6f5d97SBram Moolenaar #define SPO_HE_OFF 3 // highl. end offset
250d6f5d97SBram Moolenaar #define SPO_RS_OFF 4 // region start offset
260d6f5d97SBram Moolenaar #define SPO_RE_OFF 5 // region end offset
270d6f5d97SBram Moolenaar #define SPO_LC_OFF 6 // leading context offset
28071d4279SBram Moolenaar #define SPO_COUNT 7
29071d4279SBram Moolenaar
30071d4279SBram Moolenaar static char *(spo_name_tab[SPO_COUNT]) =
31071d4279SBram Moolenaar {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
32071d4279SBram Moolenaar
33e35a52aeSBram Moolenaar static char e_illegal_arg[] = N_("E390: Illegal argument: %s");
34e35a52aeSBram Moolenaar
35071d4279SBram Moolenaar /*
36071d4279SBram Moolenaar * The patterns that are being searched for are stored in a syn_pattern.
37071d4279SBram Moolenaar * A match item consists of one pattern.
38071d4279SBram Moolenaar * A start/end item consists of n start patterns and m end patterns.
39071d4279SBram Moolenaar * A start/skip/end item consists of n start patterns, one skip pattern and m
40071d4279SBram Moolenaar * end patterns.
41071d4279SBram Moolenaar * For the latter two, the patterns are always consecutive: start-skip-end.
42071d4279SBram Moolenaar *
43071d4279SBram Moolenaar * A character offset can be given for the matched text (_m_start and _m_end)
44071d4279SBram Moolenaar * and for the actually highlighted text (_h_start and _h_end).
4536f92301SBram Moolenaar *
4636f92301SBram Moolenaar * Note that ordering of members is optimized to reduce padding.
47071d4279SBram Moolenaar */
48071d4279SBram Moolenaar typedef struct syn_pattern
49071d4279SBram Moolenaar {
500d6f5d97SBram Moolenaar char sp_type; // see SPTYPE_ defines below
510d6f5d97SBram Moolenaar char sp_syncing; // this item used for syncing
520d6f5d97SBram Moolenaar short sp_syn_match_id; // highlight group ID of pattern
530d6f5d97SBram Moolenaar short sp_off_flags; // see below
540d6f5d97SBram Moolenaar int sp_offsets[SPO_COUNT]; // offsets
550d6f5d97SBram Moolenaar int sp_flags; // see HL_ defines below
56860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
570d6f5d97SBram Moolenaar int sp_cchar; // conceal substitute character
58860cae1cSBram Moolenaar #endif
590d6f5d97SBram Moolenaar int sp_ic; // ignore-case flag for sp_prog
600d6f5d97SBram Moolenaar int sp_sync_idx; // sync item index (syncing only)
610d6f5d97SBram Moolenaar int sp_line_id; // ID of last line where tried
620d6f5d97SBram Moolenaar int sp_startcol; // next match in sp_line_id line
630d6f5d97SBram Moolenaar short *sp_cont_list; // cont. group IDs, if non-zero
640d6f5d97SBram Moolenaar short *sp_next_list; // next group IDs, if non-zero
650d6f5d97SBram Moolenaar struct sp_syn sp_syn; // struct passed to in_id_list()
660d6f5d97SBram Moolenaar char_u *sp_pattern; // regexp to match, pattern
670d6f5d97SBram Moolenaar regprog_T *sp_prog; // regexp to match, program
68f7512552SBram Moolenaar #ifdef FEAT_PROFILE
698a7f5a2dSBram Moolenaar syn_time_T sp_time;
708a7f5a2dSBram Moolenaar #endif
71071d4279SBram Moolenaar } synpat_T;
72071d4279SBram Moolenaar
730d6f5d97SBram Moolenaar // The sp_off_flags are computed like this:
740d6f5d97SBram Moolenaar // offset from the start of the matched text: (1 << SPO_XX_OFF)
750d6f5d97SBram Moolenaar // offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
760d6f5d97SBram Moolenaar // When both are present, only one is used.
77071d4279SBram Moolenaar
780d6f5d97SBram Moolenaar #define SPTYPE_MATCH 1 // match keyword with this group ID
790d6f5d97SBram Moolenaar #define SPTYPE_START 2 // match a regexp, start of item
800d6f5d97SBram Moolenaar #define SPTYPE_END 3 // match a regexp, end of item
810d6f5d97SBram Moolenaar #define SPTYPE_SKIP 4 // match a regexp, skip within item
82071d4279SBram Moolenaar
83071d4279SBram Moolenaar
84071d4279SBram Moolenaar #define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
85071d4279SBram Moolenaar
860d6f5d97SBram Moolenaar #define NONE_IDX -2 // value of sp_sync_idx for "NONE"
87071d4279SBram Moolenaar
88071d4279SBram Moolenaar /*
89071d4279SBram Moolenaar * Flags for b_syn_sync_flags:
90071d4279SBram Moolenaar */
910d6f5d97SBram Moolenaar #define SF_CCOMMENT 0x01 // sync on a C-style comment
920d6f5d97SBram Moolenaar #define SF_MATCH 0x02 // sync by matching a pattern
93071d4279SBram Moolenaar
94071d4279SBram Moolenaar #define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
95071d4279SBram Moolenaar
960d6f5d97SBram Moolenaar #define MAXKEYWLEN 80 // maximum length of a keyword
97071d4279SBram Moolenaar
98071d4279SBram Moolenaar /*
99071d4279SBram Moolenaar * The attributes of the syntax item that has been recognized.
100071d4279SBram Moolenaar */
1010d6f5d97SBram Moolenaar static int current_attr = 0; // attr of current syntax word
102071d4279SBram Moolenaar #ifdef FEAT_EVAL
1030d6f5d97SBram Moolenaar static int current_id = 0; // ID of current char for syn_get_id()
1040d6f5d97SBram Moolenaar static int current_trans_id = 0; // idem, transparency removed
105071d4279SBram Moolenaar #endif
106860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
107860cae1cSBram Moolenaar static int current_flags = 0;
1086e202e52SBram Moolenaar static int current_seqnr = 0;
109860cae1cSBram Moolenaar static int current_sub_char = 0;
110860cae1cSBram Moolenaar #endif
111071d4279SBram Moolenaar
112217ad920SBram Moolenaar typedef struct syn_cluster_S
113071d4279SBram Moolenaar {
1140d6f5d97SBram Moolenaar char_u *scl_name; // syntax cluster name
1150d6f5d97SBram Moolenaar char_u *scl_name_u; // uppercase of scl_name
1160d6f5d97SBram Moolenaar short *scl_list; // IDs in this syntax cluster
117217ad920SBram Moolenaar } syn_cluster_T;
118071d4279SBram Moolenaar
119071d4279SBram Moolenaar /*
120071d4279SBram Moolenaar * Methods of combining two clusters
121071d4279SBram Moolenaar */
1220d6f5d97SBram Moolenaar #define CLUSTER_REPLACE 1 // replace first list with second
1230d6f5d97SBram Moolenaar #define CLUSTER_ADD 2 // add second list to first
1240d6f5d97SBram Moolenaar #define CLUSTER_SUBTRACT 3 // subtract second list from first
125071d4279SBram Moolenaar
126217ad920SBram Moolenaar #define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
127071d4279SBram Moolenaar
128071d4279SBram Moolenaar /*
129071d4279SBram Moolenaar * Syntax group IDs have different types:
13042431a7aSBram Moolenaar * 0 - 19999 normal syntax groups
13142431a7aSBram Moolenaar * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
13242431a7aSBram Moolenaar * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
13342431a7aSBram Moolenaar * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
13442431a7aSBram Moolenaar * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
135071d4279SBram Moolenaar */
1360d6f5d97SBram Moolenaar #define SYNID_ALLBUT MAX_HL_ID // syntax group ID for contains=ALLBUT
1370d6f5d97SBram Moolenaar #define SYNID_TOP 21000 // syntax group ID for contains=TOP
1380d6f5d97SBram Moolenaar #define SYNID_CONTAINED 22000 // syntax group ID for contains=CONTAINED
1390d6f5d97SBram Moolenaar #define SYNID_CLUSTER 23000 // first syntax group ID for clusters
14042431a7aSBram Moolenaar
1410d6f5d97SBram Moolenaar #define MAX_SYN_INC_TAG 999 // maximum before the above overflow
14242431a7aSBram Moolenaar #define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
143071d4279SBram Moolenaar
144071d4279SBram Moolenaar /*
145071d4279SBram Moolenaar * Annoying Hack(TM): ":syn include" needs this pointer to pass to
146071d4279SBram Moolenaar * expand_filename(). Most of the other syntax commands don't need it, so
147071d4279SBram Moolenaar * instead of passing it to them, we stow it here.
148071d4279SBram Moolenaar */
149071d4279SBram Moolenaar static char_u **syn_cmdlinep;
150071d4279SBram Moolenaar
151071d4279SBram Moolenaar /*
152071d4279SBram Moolenaar * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
15356be9500SBram Moolenaar * files from leaking into ALLBUT lists, we assign a unique ID to the
154071d4279SBram Moolenaar * rules in each ":syn include"'d file.
155071d4279SBram Moolenaar */
156071d4279SBram Moolenaar static int current_syn_inc_tag = 0;
157071d4279SBram Moolenaar static int running_syn_inc_tag = 0;
158071d4279SBram Moolenaar
159071d4279SBram Moolenaar /*
160dad6b69cSBram Moolenaar * In a hashtable item "hi_key" points to "keyword" in a keyentry.
161dad6b69cSBram Moolenaar * This avoids adding a pointer to the hashtable item.
162dad6b69cSBram Moolenaar * KE2HIKEY() converts a var pointer to a hashitem key pointer.
163dad6b69cSBram Moolenaar * HIKEY2KE() converts a hashitem key pointer to a var pointer.
164dad6b69cSBram Moolenaar * HI2KE() converts a hashitem pointer to a var pointer.
165dad6b69cSBram Moolenaar */
166dad6b69cSBram Moolenaar static keyentry_T dumkey;
167dad6b69cSBram Moolenaar #define KE2HIKEY(kp) ((kp)->keyword)
168dad6b69cSBram Moolenaar #define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
169dad6b69cSBram Moolenaar #define HI2KE(hi) HIKEY2KE((hi)->hi_key)
170dad6b69cSBram Moolenaar
171dad6b69cSBram Moolenaar /*
172071d4279SBram Moolenaar * To reduce the time spent in keepend(), remember at which level in the state
173071d4279SBram Moolenaar * stack the first item with "keepend" is present. When "-1", there is no
174071d4279SBram Moolenaar * "keepend" on the stack.
175071d4279SBram Moolenaar */
176071d4279SBram Moolenaar static int keepend_level = -1;
177071d4279SBram Moolenaar
1788a7f5a2dSBram Moolenaar static char msg_no_items[] = N_("No Syntax items defined for this buffer");
1798a7f5a2dSBram Moolenaar
180071d4279SBram Moolenaar /*
181071d4279SBram Moolenaar * For the current state we need to remember more than just the idx.
182071d4279SBram Moolenaar * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
183071d4279SBram Moolenaar * (The end positions have the column number of the next char)
184071d4279SBram Moolenaar */
185071d4279SBram Moolenaar typedef struct state_item
186071d4279SBram Moolenaar {
1870d6f5d97SBram Moolenaar int si_idx; // index of syntax pattern or
1880d6f5d97SBram Moolenaar // KEYWORD_IDX
1890d6f5d97SBram Moolenaar int si_id; // highlight group ID for keywords
1900d6f5d97SBram Moolenaar int si_trans_id; // idem, transparency removed
1910d6f5d97SBram Moolenaar int si_m_lnum; // lnum of the match
1920d6f5d97SBram Moolenaar int si_m_startcol; // starting column of the match
1930d6f5d97SBram Moolenaar lpos_T si_m_endpos; // just after end posn of the match
1940d6f5d97SBram Moolenaar lpos_T si_h_startpos; // start position of the highlighting
1950d6f5d97SBram Moolenaar lpos_T si_h_endpos; // end position of the highlighting
1960d6f5d97SBram Moolenaar lpos_T si_eoe_pos; // end position of end pattern
1970d6f5d97SBram Moolenaar int si_end_idx; // group ID for end pattern or zero
1980d6f5d97SBram Moolenaar int si_ends; // if match ends before si_m_endpos
1990d6f5d97SBram Moolenaar int si_attr; // attributes in this state
2000d6f5d97SBram Moolenaar long si_flags; // HL_HAS_EOL flag in this state, and
2010d6f5d97SBram Moolenaar // HL_SKIP* for si_next_list
202860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
2030d6f5d97SBram Moolenaar int si_seqnr; // sequence number
2040d6f5d97SBram Moolenaar int si_cchar; // substitution character for conceal
205860cae1cSBram Moolenaar #endif
2060d6f5d97SBram Moolenaar short *si_cont_list; // list of contained groups
2070d6f5d97SBram Moolenaar short *si_next_list; // nextgroup IDs after this item ends
2080d6f5d97SBram Moolenaar reg_extmatch_T *si_extmatch; // \z(...\) matches from start
2090d6f5d97SBram Moolenaar // pattern
210071d4279SBram Moolenaar } stateitem_T;
211071d4279SBram Moolenaar
2120d6f5d97SBram Moolenaar #define KEYWORD_IDX -1 // value of si_idx for keywords
2130d6f5d97SBram Moolenaar #define ID_LIST_ALL (short *)-1 // valid of si_cont_list for containing all
2140d6f5d97SBram Moolenaar // but contained groups
215071d4279SBram Moolenaar
216ffbbcb59SBram Moolenaar #ifdef FEAT_CONCEAL
2170d6f5d97SBram Moolenaar static int next_seqnr = 1; // value to use for si_seqnr
218ffbbcb59SBram Moolenaar #endif
219ffbbcb59SBram Moolenaar
220071d4279SBram Moolenaar /*
2216ac5429dSBram Moolenaar * Struct to reduce the number of arguments to get_syn_options(), it's used
2226ac5429dSBram Moolenaar * very often.
2236ac5429dSBram Moolenaar */
2246ac5429dSBram Moolenaar typedef struct
2256ac5429dSBram Moolenaar {
2260d6f5d97SBram Moolenaar int flags; // flags for contained and transparent
2270d6f5d97SBram Moolenaar int keyword; // TRUE for ":syn keyword"
2280d6f5d97SBram Moolenaar int *sync_idx; // syntax item for "grouphere" argument, NULL
2290d6f5d97SBram Moolenaar // if not allowed
2300d6f5d97SBram Moolenaar char has_cont_list; // TRUE if "cont_list" can be used
2310d6f5d97SBram Moolenaar short *cont_list; // group IDs for "contains" argument
2320d6f5d97SBram Moolenaar short *cont_in_list; // group IDs for "containedin" argument
2330d6f5d97SBram Moolenaar short *next_list; // group IDs for "nextgroup" argument
2346ac5429dSBram Moolenaar } syn_opt_arg_T;
2356ac5429dSBram Moolenaar
2366ac5429dSBram Moolenaar /*
237071d4279SBram Moolenaar * The next possible match in the current line for any pattern is remembered,
238071d4279SBram Moolenaar * to avoid having to try for a match in each column.
239071d4279SBram Moolenaar * If next_match_idx == -1, not tried (in this line) yet.
240071d4279SBram Moolenaar * If next_match_col == MAXCOL, no match found in this line.
241071d4279SBram Moolenaar * (All end positions have the column of the char after the end)
242071d4279SBram Moolenaar */
2430d6f5d97SBram Moolenaar static int next_match_col; // column for start of next match
2440d6f5d97SBram Moolenaar static lpos_T next_match_m_endpos; // position for end of next match
2450d6f5d97SBram Moolenaar static lpos_T next_match_h_startpos; // pos. for highl. start of next match
2460d6f5d97SBram Moolenaar static lpos_T next_match_h_endpos; // pos. for highl. end of next match
2470d6f5d97SBram Moolenaar static int next_match_idx; // index of matched item
2480d6f5d97SBram Moolenaar static long next_match_flags; // flags for next match
2490d6f5d97SBram Moolenaar static lpos_T next_match_eos_pos; // end of start pattn (start region)
2500d6f5d97SBram Moolenaar static lpos_T next_match_eoe_pos; // pos. for end of end pattern
2510d6f5d97SBram Moolenaar static int next_match_end_idx; // ID of group for end pattn or zero
252071d4279SBram Moolenaar static reg_extmatch_T *next_match_extmatch = NULL;
253071d4279SBram Moolenaar
254071d4279SBram Moolenaar /*
255071d4279SBram Moolenaar * A state stack is an array of integers or stateitem_T, stored in a
256f86db78fSBram Moolenaar * garray_T. A state stack is invalid if its itemsize entry is zero.
257071d4279SBram Moolenaar */
258071d4279SBram Moolenaar #define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
259071d4279SBram Moolenaar #define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
260071d4279SBram Moolenaar
26100d253e2SBram Moolenaar #define FOR_ALL_SYNSTATES(sb, sst) \
26200d253e2SBram Moolenaar for ((sst) = (sb)->b_sst_first; (sst) != NULL; (sst) = (sst)->sst_next)
26300d253e2SBram Moolenaar
264071d4279SBram Moolenaar /*
265071d4279SBram Moolenaar * The current state (within the line) of the recognition engine.
266071d4279SBram Moolenaar * When current_state.ga_itemsize is 0 the current state is invalid.
267071d4279SBram Moolenaar */
2680d6f5d97SBram Moolenaar static win_T *syn_win; // current window for highlighting
2690d6f5d97SBram Moolenaar static buf_T *syn_buf; // current buffer for highlighting
2700d6f5d97SBram Moolenaar static synblock_T *syn_block; // current buffer for highlighting
27106f1ed2fSBram Moolenaar #ifdef FEAT_RELTIME
2720d6f5d97SBram Moolenaar static proftime_T *syn_tm; // timeout limit
27306f1ed2fSBram Moolenaar #endif
2740d6f5d97SBram Moolenaar static linenr_T current_lnum = 0; // lnum of current state
2750d6f5d97SBram Moolenaar static colnr_T current_col = 0; // column of current state
2760d6f5d97SBram Moolenaar static int current_state_stored = 0; // TRUE if stored current state
2770d6f5d97SBram Moolenaar // after setting current_finished
2780d6f5d97SBram Moolenaar static int current_finished = 0; // current line has been finished
2790d6f5d97SBram Moolenaar static garray_T current_state // current stack of state_items
280071d4279SBram Moolenaar = {0, 0, 0, 0, NULL};
2810d6f5d97SBram Moolenaar static short *current_next_list = NULL; // when non-zero, nextgroup list
2820d6f5d97SBram Moolenaar static int current_next_flags = 0; // flags for current_next_list
2830d6f5d97SBram Moolenaar static int current_line_id = 0; // unique number for current line
284071d4279SBram Moolenaar
285071d4279SBram Moolenaar #define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
286071d4279SBram Moolenaar
287baaa7e9eSBram Moolenaar static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
288baaa7e9eSBram Moolenaar static int syn_match_linecont(linenr_T lnum);
289baaa7e9eSBram Moolenaar static void syn_start_line(void);
290baaa7e9eSBram Moolenaar static void syn_update_ends(int startofline);
291baaa7e9eSBram Moolenaar static void syn_stack_alloc(void);
292baaa7e9eSBram Moolenaar static int syn_stack_cleanup(void);
293baaa7e9eSBram Moolenaar static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
294baaa7e9eSBram Moolenaar static synstate_T *syn_stack_find_entry(linenr_T lnum);
295baaa7e9eSBram Moolenaar static synstate_T *store_current_state(void);
296baaa7e9eSBram Moolenaar static void load_current_state(synstate_T *from);
297baaa7e9eSBram Moolenaar static void invalidate_current_state(void);
298baaa7e9eSBram Moolenaar static int syn_stack_equal(synstate_T *sp);
299baaa7e9eSBram Moolenaar static void validate_current_state(void);
300baaa7e9eSBram Moolenaar static int syn_finish_line(int syncing);
301baaa7e9eSBram Moolenaar static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
302baaa7e9eSBram Moolenaar static int did_match_already(int idx, garray_T *gap);
303baaa7e9eSBram Moolenaar static stateitem_T *push_next_match(stateitem_T *cur_si);
304baaa7e9eSBram Moolenaar static void check_state_ends(void);
305baaa7e9eSBram Moolenaar static void update_si_attr(int idx);
306baaa7e9eSBram Moolenaar static void check_keepend(void);
307baaa7e9eSBram Moolenaar static void update_si_end(stateitem_T *sip, int startcol, int force);
308baaa7e9eSBram Moolenaar static short *copy_id_list(short *list);
309baaa7e9eSBram Moolenaar static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
310baaa7e9eSBram Moolenaar static int push_current_state(int idx);
311baaa7e9eSBram Moolenaar static void pop_current_state(void);
312f7512552SBram Moolenaar #ifdef FEAT_PROFILE
313baaa7e9eSBram Moolenaar static void syn_clear_time(syn_time_T *tt);
314baaa7e9eSBram Moolenaar static void syntime_clear(void);
315baaa7e9eSBram Moolenaar static void syntime_report(void);
3168a7f5a2dSBram Moolenaar static int syn_time_on = FALSE;
3178a7f5a2dSBram Moolenaar # define IF_SYN_TIME(p) (p)
3188a7f5a2dSBram Moolenaar #else
3198a7f5a2dSBram Moolenaar # define IF_SYN_TIME(p) NULL
3208a7f5a2dSBram Moolenaar typedef int syn_time_T;
3218a7f5a2dSBram Moolenaar #endif
322071d4279SBram Moolenaar
323baaa7e9eSBram Moolenaar static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
324baaa7e9eSBram Moolenaar static 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);
325071d4279SBram Moolenaar
326baaa7e9eSBram Moolenaar static void limit_pos(lpos_T *pos, lpos_T *limit);
327baaa7e9eSBram Moolenaar static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
328baaa7e9eSBram Moolenaar static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
329baaa7e9eSBram Moolenaar static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
330baaa7e9eSBram Moolenaar static char_u *syn_getcurline(void);
331baaa7e9eSBram Moolenaar static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
332baaa7e9eSBram Moolenaar static int check_keyword_id(char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp);
333baaa7e9eSBram Moolenaar static void syn_remove_pattern(synblock_T *block, int idx);
334baaa7e9eSBram Moolenaar static void syn_clear_pattern(synblock_T *block, int i);
335baaa7e9eSBram Moolenaar static void syn_clear_cluster(synblock_T *block, int i);
336baaa7e9eSBram Moolenaar static void syn_clear_one(int id, int syncing);
337baaa7e9eSBram Moolenaar static void syn_cmd_onoff(exarg_T *eap, char *name);
338baaa7e9eSBram Moolenaar static void syn_lines_msg(void);
339baaa7e9eSBram Moolenaar static void syn_match_msg(void);
340baaa7e9eSBram Moolenaar static void syn_list_one(int id, int syncing, int link_only);
341baaa7e9eSBram Moolenaar static void syn_list_cluster(int id);
342baaa7e9eSBram Moolenaar static void put_id_list(char_u *name, short *list, int attr);
343baaa7e9eSBram Moolenaar static void put_pattern(char *s, int c, synpat_T *spp, int attr);
344baaa7e9eSBram Moolenaar static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
345baaa7e9eSBram Moolenaar static void syn_clear_keyword(int id, hashtab_T *ht);
346baaa7e9eSBram Moolenaar static void clear_keywtab(hashtab_T *ht);
347baaa7e9eSBram Moolenaar static int syn_scl_namen2id(char_u *linep, int len);
348baaa7e9eSBram Moolenaar static int syn_check_cluster(char_u *pp, int len);
349baaa7e9eSBram Moolenaar static int syn_add_cluster(char_u *name);
350baaa7e9eSBram Moolenaar static void init_syn_patterns(void);
351baaa7e9eSBram Moolenaar static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
352de318c5cSBram Moolenaar static int get_id_list(char_u **arg, int keylen, short **list, int skip);
353baaa7e9eSBram Moolenaar static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
354071d4279SBram Moolenaar
355f3d769a5SBram Moolenaar #if defined(FEAT_RELTIME) || defined(PROTO)
356f3d769a5SBram Moolenaar /*
357f3d769a5SBram Moolenaar * Set the timeout used for syntax highlighting.
358f3d769a5SBram Moolenaar * Use NULL to reset, no timeout.
359f3d769a5SBram Moolenaar */
360f3d769a5SBram Moolenaar void
syn_set_timeout(proftime_T * tm)361f3d769a5SBram Moolenaar syn_set_timeout(proftime_T *tm)
362f3d769a5SBram Moolenaar {
363f3d769a5SBram Moolenaar syn_tm = tm;
364f3d769a5SBram Moolenaar }
365f3d769a5SBram Moolenaar #endif
366f3d769a5SBram Moolenaar
367071d4279SBram Moolenaar /*
368071d4279SBram Moolenaar * Start the syntax recognition for a line. This function is normally called
369071d4279SBram Moolenaar * from the screen updating, once for each displayed line.
370071d4279SBram Moolenaar * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
371071d4279SBram Moolenaar * it. Careful: curbuf and curwin are likely to point to another buffer and
372071d4279SBram Moolenaar * window.
373071d4279SBram Moolenaar */
374071d4279SBram Moolenaar void
syntax_start(win_T * wp,linenr_T lnum)375f3d769a5SBram Moolenaar syntax_start(win_T *wp, linenr_T lnum)
376071d4279SBram Moolenaar {
377071d4279SBram Moolenaar synstate_T *p;
378071d4279SBram Moolenaar synstate_T *last_valid = NULL;
379071d4279SBram Moolenaar synstate_T *last_min_valid = NULL;
380dbe31750SBram Moolenaar synstate_T *sp, *prev = NULL;
381071d4279SBram Moolenaar linenr_T parsed_lnum;
382071d4279SBram Moolenaar linenr_T first_stored;
383071d4279SBram Moolenaar int dist;
3840d6f5d97SBram Moolenaar static varnumber_T changedtick = 0; // remember the last change ID
385071d4279SBram Moolenaar
3867510fe74SBram Moolenaar #ifdef FEAT_CONCEAL
3877510fe74SBram Moolenaar current_sub_char = NUL;
3887510fe74SBram Moolenaar #endif
3897510fe74SBram Moolenaar
390071d4279SBram Moolenaar /*
391071d4279SBram Moolenaar * After switching buffers, invalidate current_state.
3923fdfa4a9SBram Moolenaar * Also do this when a change was made, the current state may be invalid
3933fdfa4a9SBram Moolenaar * then.
394071d4279SBram Moolenaar */
395b681be17SBram Moolenaar if (syn_block != wp->w_s
396b681be17SBram Moolenaar || syn_buf != wp->w_buffer
39795c526e1SBram Moolenaar || changedtick != CHANGEDTICK(syn_buf))
398071d4279SBram Moolenaar {
399071d4279SBram Moolenaar invalidate_current_state();
400071d4279SBram Moolenaar syn_buf = wp->w_buffer;
401860cae1cSBram Moolenaar syn_block = wp->w_s;
402071d4279SBram Moolenaar }
40395c526e1SBram Moolenaar changedtick = CHANGEDTICK(syn_buf);
404071d4279SBram Moolenaar syn_win = wp;
405071d4279SBram Moolenaar
406071d4279SBram Moolenaar /*
407071d4279SBram Moolenaar * Allocate syntax stack when needed.
408071d4279SBram Moolenaar */
409071d4279SBram Moolenaar syn_stack_alloc();
410860cae1cSBram Moolenaar if (syn_block->b_sst_array == NULL)
4110d6f5d97SBram Moolenaar return; // out of memory
412860cae1cSBram Moolenaar syn_block->b_sst_lasttick = display_tick;
413071d4279SBram Moolenaar
414071d4279SBram Moolenaar /*
415071d4279SBram Moolenaar * If the state of the end of the previous line is useful, store it.
416071d4279SBram Moolenaar */
417071d4279SBram Moolenaar if (VALID_STATE(¤t_state)
418071d4279SBram Moolenaar && current_lnum < lnum
419071d4279SBram Moolenaar && current_lnum < syn_buf->b_ml.ml_line_count)
420071d4279SBram Moolenaar {
421071d4279SBram Moolenaar (void)syn_finish_line(FALSE);
422071d4279SBram Moolenaar if (!current_state_stored)
423071d4279SBram Moolenaar {
424071d4279SBram Moolenaar ++current_lnum;
425dbe31750SBram Moolenaar (void)store_current_state();
426071d4279SBram Moolenaar }
427071d4279SBram Moolenaar
428071d4279SBram Moolenaar /*
429071d4279SBram Moolenaar * If the current_lnum is now the same as "lnum", keep the current
430071d4279SBram Moolenaar * state (this happens very often!). Otherwise invalidate
431071d4279SBram Moolenaar * current_state and figure it out below.
432071d4279SBram Moolenaar */
433071d4279SBram Moolenaar if (current_lnum != lnum)
434071d4279SBram Moolenaar invalidate_current_state();
435071d4279SBram Moolenaar }
436071d4279SBram Moolenaar else
437071d4279SBram Moolenaar invalidate_current_state();
438071d4279SBram Moolenaar
439071d4279SBram Moolenaar /*
440071d4279SBram Moolenaar * Try to synchronize from a saved state in b_sst_array[].
441071d4279SBram Moolenaar * Only do this if lnum is not before and not to far beyond a saved state.
442071d4279SBram Moolenaar */
443860cae1cSBram Moolenaar if (INVALID_STATE(¤t_state) && syn_block->b_sst_array != NULL)
444071d4279SBram Moolenaar {
4450d6f5d97SBram Moolenaar // Find last valid saved state before start_lnum.
44600d253e2SBram Moolenaar FOR_ALL_SYNSTATES(syn_block, p)
447071d4279SBram Moolenaar {
448071d4279SBram Moolenaar if (p->sst_lnum > lnum)
449071d4279SBram Moolenaar break;
450071d4279SBram Moolenaar if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
451071d4279SBram Moolenaar {
452071d4279SBram Moolenaar last_valid = p;
453860cae1cSBram Moolenaar if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
454071d4279SBram Moolenaar last_min_valid = p;
455071d4279SBram Moolenaar }
456071d4279SBram Moolenaar }
457071d4279SBram Moolenaar if (last_min_valid != NULL)
458071d4279SBram Moolenaar load_current_state(last_min_valid);
459071d4279SBram Moolenaar }
460071d4279SBram Moolenaar
461071d4279SBram Moolenaar /*
462071d4279SBram Moolenaar * If "lnum" is before or far beyond a line with a saved state, need to
463071d4279SBram Moolenaar * re-synchronize.
464071d4279SBram Moolenaar */
465071d4279SBram Moolenaar if (INVALID_STATE(¤t_state))
466071d4279SBram Moolenaar {
467071d4279SBram Moolenaar syn_sync(wp, lnum, last_valid);
468d6761c3cSBram Moolenaar if (current_lnum == 1)
4690d6f5d97SBram Moolenaar // First line is always valid, no matter "minlines".
470d6761c3cSBram Moolenaar first_stored = 1;
471d6761c3cSBram Moolenaar else
4720d6f5d97SBram Moolenaar // Need to parse "minlines" lines before state can be considered
4730d6f5d97SBram Moolenaar // valid to store.
474860cae1cSBram Moolenaar first_stored = current_lnum + syn_block->b_syn_sync_minlines;
475071d4279SBram Moolenaar }
476071d4279SBram Moolenaar else
477071d4279SBram Moolenaar first_stored = current_lnum;
478071d4279SBram Moolenaar
479071d4279SBram Moolenaar /*
480071d4279SBram Moolenaar * Advance from the sync point or saved state until the current line.
481071d4279SBram Moolenaar * Save some entries for syncing with later on.
482071d4279SBram Moolenaar */
483860cae1cSBram Moolenaar if (syn_block->b_sst_len <= Rows)
484910f66f9SBram Moolenaar dist = 999999;
485910f66f9SBram Moolenaar else
486860cae1cSBram Moolenaar dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
487071d4279SBram Moolenaar while (current_lnum < lnum)
488071d4279SBram Moolenaar {
489071d4279SBram Moolenaar syn_start_line();
490071d4279SBram Moolenaar (void)syn_finish_line(FALSE);
491071d4279SBram Moolenaar ++current_lnum;
492071d4279SBram Moolenaar
4930d6f5d97SBram Moolenaar // If we parsed at least "minlines" lines or started at a valid
4940d6f5d97SBram Moolenaar // state, the current state is considered valid.
495071d4279SBram Moolenaar if (current_lnum >= first_stored)
496071d4279SBram Moolenaar {
4970d6f5d97SBram Moolenaar // Check if the saved state entry is for the current line and is
4980d6f5d97SBram Moolenaar // equal to the current state. If so, then validate all saved
4990d6f5d97SBram Moolenaar // states that depended on a change before the parsed line.
500071d4279SBram Moolenaar if (prev == NULL)
501dbe31750SBram Moolenaar prev = syn_stack_find_entry(current_lnum - 1);
502dbe31750SBram Moolenaar if (prev == NULL)
503860cae1cSBram Moolenaar sp = syn_block->b_sst_first;
504071d4279SBram Moolenaar else
505dbe31750SBram Moolenaar sp = prev;
506dbe31750SBram Moolenaar while (sp != NULL && sp->sst_lnum < current_lnum)
507dbe31750SBram Moolenaar sp = sp->sst_next;
508071d4279SBram Moolenaar if (sp != NULL
509071d4279SBram Moolenaar && sp->sst_lnum == current_lnum
510071d4279SBram Moolenaar && syn_stack_equal(sp))
511071d4279SBram Moolenaar {
512071d4279SBram Moolenaar parsed_lnum = current_lnum;
513071d4279SBram Moolenaar prev = sp;
514071d4279SBram Moolenaar while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
515071d4279SBram Moolenaar {
516071d4279SBram Moolenaar if (sp->sst_lnum <= lnum)
5170d6f5d97SBram Moolenaar // valid state before desired line, use this one
518071d4279SBram Moolenaar prev = sp;
519071d4279SBram Moolenaar else if (sp->sst_change_lnum == 0)
5200d6f5d97SBram Moolenaar // past saved states depending on change, break here.
521071d4279SBram Moolenaar break;
522071d4279SBram Moolenaar sp->sst_change_lnum = 0;
523071d4279SBram Moolenaar sp = sp->sst_next;
524071d4279SBram Moolenaar }
525071d4279SBram Moolenaar load_current_state(prev);
526071d4279SBram Moolenaar }
5270d6f5d97SBram Moolenaar // Store the state at this line when it's the first one, the line
5280d6f5d97SBram Moolenaar // where we start parsing, or some distance from the previously
5290d6f5d97SBram Moolenaar // saved state. But only when parsed at least 'minlines'.
530071d4279SBram Moolenaar else if (prev == NULL
531071d4279SBram Moolenaar || current_lnum == lnum
532071d4279SBram Moolenaar || current_lnum >= prev->sst_lnum + dist)
533dbe31750SBram Moolenaar prev = store_current_state();
534071d4279SBram Moolenaar }
535071d4279SBram Moolenaar
5360d6f5d97SBram Moolenaar // This can take a long time: break when CTRL-C pressed. The current
5370d6f5d97SBram Moolenaar // state will be wrong then.
538071d4279SBram Moolenaar line_breakcheck();
539071d4279SBram Moolenaar if (got_int)
540071d4279SBram Moolenaar {
541071d4279SBram Moolenaar current_lnum = lnum;
542071d4279SBram Moolenaar break;
543071d4279SBram Moolenaar }
544071d4279SBram Moolenaar }
545071d4279SBram Moolenaar
546071d4279SBram Moolenaar syn_start_line();
547071d4279SBram Moolenaar }
548071d4279SBram Moolenaar
549071d4279SBram Moolenaar /*
550071d4279SBram Moolenaar * We cannot simply discard growarrays full of state_items or buf_states; we
551071d4279SBram Moolenaar * have to manually release their extmatch pointers first.
552071d4279SBram Moolenaar */
553071d4279SBram Moolenaar static void
clear_syn_state(synstate_T * p)554764b23c8SBram Moolenaar clear_syn_state(synstate_T *p)
555071d4279SBram Moolenaar {
556071d4279SBram Moolenaar int i;
557071d4279SBram Moolenaar garray_T *gap;
558071d4279SBram Moolenaar
559071d4279SBram Moolenaar if (p->sst_stacksize > SST_FIX_STATES)
560071d4279SBram Moolenaar {
561071d4279SBram Moolenaar gap = &(p->sst_union.sst_ga);
562071d4279SBram Moolenaar for (i = 0; i < gap->ga_len; i++)
563071d4279SBram Moolenaar unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
564071d4279SBram Moolenaar ga_clear(gap);
565071d4279SBram Moolenaar }
566071d4279SBram Moolenaar else
567071d4279SBram Moolenaar {
568071d4279SBram Moolenaar for (i = 0; i < p->sst_stacksize; i++)
569071d4279SBram Moolenaar unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
570071d4279SBram Moolenaar }
571071d4279SBram Moolenaar }
572071d4279SBram Moolenaar
573071d4279SBram Moolenaar /*
574071d4279SBram Moolenaar * Cleanup the current_state stack.
575071d4279SBram Moolenaar */
576071d4279SBram Moolenaar static void
clear_current_state(void)577764b23c8SBram Moolenaar clear_current_state(void)
578071d4279SBram Moolenaar {
579071d4279SBram Moolenaar int i;
580071d4279SBram Moolenaar stateitem_T *sip;
581071d4279SBram Moolenaar
582071d4279SBram Moolenaar sip = (stateitem_T *)(current_state.ga_data);
583071d4279SBram Moolenaar for (i = 0; i < current_state.ga_len; i++)
584071d4279SBram Moolenaar unref_extmatch(sip[i].si_extmatch);
585071d4279SBram Moolenaar ga_clear(¤t_state);
586071d4279SBram Moolenaar }
587071d4279SBram Moolenaar
588071d4279SBram Moolenaar /*
589071d4279SBram Moolenaar * Try to find a synchronisation point for line "lnum".
590071d4279SBram Moolenaar *
591071d4279SBram Moolenaar * This sets current_lnum and the current state. One of three methods is
592071d4279SBram Moolenaar * used:
593071d4279SBram Moolenaar * 1. Search backwards for the end of a C-comment.
594071d4279SBram Moolenaar * 2. Search backwards for given sync patterns.
595071d4279SBram Moolenaar * 3. Simply start on a given number of lines above "lnum".
596071d4279SBram Moolenaar */
597071d4279SBram Moolenaar static void
syn_sync(win_T * wp,linenr_T start_lnum,synstate_T * last_valid)598764b23c8SBram Moolenaar syn_sync(
599764b23c8SBram Moolenaar win_T *wp,
600764b23c8SBram Moolenaar linenr_T start_lnum,
601764b23c8SBram Moolenaar synstate_T *last_valid)
602071d4279SBram Moolenaar {
603071d4279SBram Moolenaar buf_T *curbuf_save;
604071d4279SBram Moolenaar win_T *curwin_save;
605071d4279SBram Moolenaar pos_T cursor_save;
606071d4279SBram Moolenaar int idx;
607071d4279SBram Moolenaar linenr_T lnum;
608071d4279SBram Moolenaar linenr_T end_lnum;
609071d4279SBram Moolenaar linenr_T break_lnum;
610071d4279SBram Moolenaar int had_sync_point;
611071d4279SBram Moolenaar stateitem_T *cur_si;
612071d4279SBram Moolenaar synpat_T *spp;
613071d4279SBram Moolenaar char_u *line;
614071d4279SBram Moolenaar int found_flags = 0;
615071d4279SBram Moolenaar int found_match_idx = 0;
616071d4279SBram Moolenaar linenr_T found_current_lnum = 0;
617071d4279SBram Moolenaar int found_current_col= 0;
618071d4279SBram Moolenaar lpos_T found_m_endpos;
61981366db6SBram Moolenaar colnr_T prev_current_col;
620071d4279SBram Moolenaar
621071d4279SBram Moolenaar /*
622071d4279SBram Moolenaar * Clear any current state that might be hanging around.
623071d4279SBram Moolenaar */
624071d4279SBram Moolenaar invalidate_current_state();
625071d4279SBram Moolenaar
626071d4279SBram Moolenaar /*
627071d4279SBram Moolenaar * Start at least "minlines" back. Default starting point for parsing is
628071d4279SBram Moolenaar * there.
629071d4279SBram Moolenaar * Start further back, to avoid that scrolling backwards will result in
630071d4279SBram Moolenaar * resyncing for every line. Now it resyncs only one out of N lines,
631071d4279SBram Moolenaar * where N is minlines * 1.5, or minlines * 2 if minlines is small.
632071d4279SBram Moolenaar * Watch out for overflow when minlines is MAXLNUM.
633071d4279SBram Moolenaar */
634860cae1cSBram Moolenaar if (syn_block->b_syn_sync_minlines > start_lnum)
635071d4279SBram Moolenaar start_lnum = 1;
636071d4279SBram Moolenaar else
637071d4279SBram Moolenaar {
638860cae1cSBram Moolenaar if (syn_block->b_syn_sync_minlines == 1)
639071d4279SBram Moolenaar lnum = 1;
640860cae1cSBram Moolenaar else if (syn_block->b_syn_sync_minlines < 10)
641860cae1cSBram Moolenaar lnum = syn_block->b_syn_sync_minlines * 2;
642071d4279SBram Moolenaar else
643860cae1cSBram Moolenaar lnum = syn_block->b_syn_sync_minlines * 3 / 2;
644860cae1cSBram Moolenaar if (syn_block->b_syn_sync_maxlines != 0
645860cae1cSBram Moolenaar && lnum > syn_block->b_syn_sync_maxlines)
646860cae1cSBram Moolenaar lnum = syn_block->b_syn_sync_maxlines;
647071d4279SBram Moolenaar if (lnum >= start_lnum)
648071d4279SBram Moolenaar start_lnum = 1;
649071d4279SBram Moolenaar else
650071d4279SBram Moolenaar start_lnum -= lnum;
651071d4279SBram Moolenaar }
652071d4279SBram Moolenaar current_lnum = start_lnum;
653071d4279SBram Moolenaar
654071d4279SBram Moolenaar /*
655071d4279SBram Moolenaar * 1. Search backwards for the end of a C-style comment.
656071d4279SBram Moolenaar */
657860cae1cSBram Moolenaar if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
658071d4279SBram Moolenaar {
6590d6f5d97SBram Moolenaar // Need to make syn_buf the current buffer for a moment, to be able to
6600d6f5d97SBram Moolenaar // use find_start_comment().
661071d4279SBram Moolenaar curwin_save = curwin;
662071d4279SBram Moolenaar curwin = wp;
663071d4279SBram Moolenaar curbuf_save = curbuf;
664071d4279SBram Moolenaar curbuf = syn_buf;
665071d4279SBram Moolenaar
666071d4279SBram Moolenaar /*
667071d4279SBram Moolenaar * Skip lines that end in a backslash.
668071d4279SBram Moolenaar */
669071d4279SBram Moolenaar for ( ; start_lnum > 1; --start_lnum)
670071d4279SBram Moolenaar {
671071d4279SBram Moolenaar line = ml_get(start_lnum - 1);
672071d4279SBram Moolenaar if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
673071d4279SBram Moolenaar break;
674071d4279SBram Moolenaar }
675071d4279SBram Moolenaar current_lnum = start_lnum;
676071d4279SBram Moolenaar
6770d6f5d97SBram Moolenaar // set cursor to start of search
678071d4279SBram Moolenaar cursor_save = wp->w_cursor;
679071d4279SBram Moolenaar wp->w_cursor.lnum = start_lnum;
680071d4279SBram Moolenaar wp->w_cursor.col = 0;
681071d4279SBram Moolenaar
682071d4279SBram Moolenaar /*
683071d4279SBram Moolenaar * If the line is inside a comment, need to find the syntax item that
684071d4279SBram Moolenaar * defines the comment.
685071d4279SBram Moolenaar * Restrict the search for the end of a comment to b_syn_sync_maxlines.
686071d4279SBram Moolenaar */
687860cae1cSBram Moolenaar if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
688071d4279SBram Moolenaar {
689860cae1cSBram Moolenaar for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
690860cae1cSBram Moolenaar if (SYN_ITEMS(syn_block)[idx].sp_syn.id
691860cae1cSBram Moolenaar == syn_block->b_syn_sync_id
692860cae1cSBram Moolenaar && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
693071d4279SBram Moolenaar {
694071d4279SBram Moolenaar validate_current_state();
695071d4279SBram Moolenaar if (push_current_state(idx) == OK)
696071d4279SBram Moolenaar update_si_attr(current_state.ga_len - 1);
697071d4279SBram Moolenaar break;
698071d4279SBram Moolenaar }
699071d4279SBram Moolenaar }
700071d4279SBram Moolenaar
7010d6f5d97SBram Moolenaar // restore cursor and buffer
702071d4279SBram Moolenaar wp->w_cursor = cursor_save;
703071d4279SBram Moolenaar curwin = curwin_save;
704071d4279SBram Moolenaar curbuf = curbuf_save;
705071d4279SBram Moolenaar }
706071d4279SBram Moolenaar
707071d4279SBram Moolenaar /*
708071d4279SBram Moolenaar * 2. Search backwards for given sync patterns.
709071d4279SBram Moolenaar */
710860cae1cSBram Moolenaar else if (syn_block->b_syn_sync_flags & SF_MATCH)
711071d4279SBram Moolenaar {
712860cae1cSBram Moolenaar if (syn_block->b_syn_sync_maxlines != 0
713860cae1cSBram Moolenaar && start_lnum > syn_block->b_syn_sync_maxlines)
714860cae1cSBram Moolenaar break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
715071d4279SBram Moolenaar else
716071d4279SBram Moolenaar break_lnum = 0;
717071d4279SBram Moolenaar
718fd2ac767SBram Moolenaar found_m_endpos.lnum = 0;
719fd2ac767SBram Moolenaar found_m_endpos.col = 0;
720071d4279SBram Moolenaar end_lnum = start_lnum;
721071d4279SBram Moolenaar lnum = start_lnum;
722071d4279SBram Moolenaar while (--lnum > break_lnum)
723071d4279SBram Moolenaar {
7240d6f5d97SBram Moolenaar // This can take a long time: break when CTRL-C pressed.
725071d4279SBram Moolenaar line_breakcheck();
726071d4279SBram Moolenaar if (got_int)
727071d4279SBram Moolenaar {
728071d4279SBram Moolenaar invalidate_current_state();
729071d4279SBram Moolenaar current_lnum = start_lnum;
730071d4279SBram Moolenaar break;
731071d4279SBram Moolenaar }
732071d4279SBram Moolenaar
7330d6f5d97SBram Moolenaar // Check if we have run into a valid saved state stack now.
734071d4279SBram Moolenaar if (last_valid != NULL && lnum == last_valid->sst_lnum)
735071d4279SBram Moolenaar {
736071d4279SBram Moolenaar load_current_state(last_valid);
737071d4279SBram Moolenaar break;
738071d4279SBram Moolenaar }
739071d4279SBram Moolenaar
740071d4279SBram Moolenaar /*
741071d4279SBram Moolenaar * Check if the previous line has the line-continuation pattern.
742071d4279SBram Moolenaar */
743071d4279SBram Moolenaar if (lnum > 1 && syn_match_linecont(lnum - 1))
744071d4279SBram Moolenaar continue;
745071d4279SBram Moolenaar
746071d4279SBram Moolenaar /*
747071d4279SBram Moolenaar * Start with nothing on the state stack
748071d4279SBram Moolenaar */
749071d4279SBram Moolenaar validate_current_state();
750071d4279SBram Moolenaar
751071d4279SBram Moolenaar for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
752071d4279SBram Moolenaar {
753071d4279SBram Moolenaar syn_start_line();
754071d4279SBram Moolenaar for (;;)
755071d4279SBram Moolenaar {
756071d4279SBram Moolenaar had_sync_point = syn_finish_line(TRUE);
757071d4279SBram Moolenaar /*
758071d4279SBram Moolenaar * When a sync point has been found, remember where, and
759071d4279SBram Moolenaar * continue to look for another one, further on in the line.
760071d4279SBram Moolenaar */
761071d4279SBram Moolenaar if (had_sync_point && current_state.ga_len)
762071d4279SBram Moolenaar {
763071d4279SBram Moolenaar cur_si = &CUR_STATE(current_state.ga_len - 1);
764071d4279SBram Moolenaar if (cur_si->si_m_endpos.lnum > start_lnum)
765071d4279SBram Moolenaar {
7660d6f5d97SBram Moolenaar // ignore match that goes to after where started
767071d4279SBram Moolenaar current_lnum = end_lnum;
768071d4279SBram Moolenaar break;
769071d4279SBram Moolenaar }
7703a36cf7bSBram Moolenaar if (cur_si->si_idx < 0)
7713a36cf7bSBram Moolenaar {
7720d6f5d97SBram Moolenaar // Cannot happen?
7733a36cf7bSBram Moolenaar found_flags = 0;
7743a36cf7bSBram Moolenaar found_match_idx = KEYWORD_IDX;
7753a36cf7bSBram Moolenaar }
7763a36cf7bSBram Moolenaar else
7773a36cf7bSBram Moolenaar {
778860cae1cSBram Moolenaar spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
779071d4279SBram Moolenaar found_flags = spp->sp_flags;
780071d4279SBram Moolenaar found_match_idx = spp->sp_sync_idx;
7813a36cf7bSBram Moolenaar }
782071d4279SBram Moolenaar found_current_lnum = current_lnum;
783071d4279SBram Moolenaar found_current_col = current_col;
784071d4279SBram Moolenaar found_m_endpos = cur_si->si_m_endpos;
785071d4279SBram Moolenaar /*
786071d4279SBram Moolenaar * Continue after the match (be aware of a zero-length
787071d4279SBram Moolenaar * match).
788071d4279SBram Moolenaar */
789071d4279SBram Moolenaar if (found_m_endpos.lnum > current_lnum)
790071d4279SBram Moolenaar {
791071d4279SBram Moolenaar current_lnum = found_m_endpos.lnum;
792071d4279SBram Moolenaar current_col = found_m_endpos.col;
793071d4279SBram Moolenaar if (current_lnum >= end_lnum)
794071d4279SBram Moolenaar break;
795071d4279SBram Moolenaar }
796071d4279SBram Moolenaar else if (found_m_endpos.col > current_col)
797071d4279SBram Moolenaar current_col = found_m_endpos.col;
798071d4279SBram Moolenaar else
799071d4279SBram Moolenaar ++current_col;
800071d4279SBram Moolenaar
8010d6f5d97SBram Moolenaar // syn_current_attr() will have skipped the check for
8020d6f5d97SBram Moolenaar // an item that ends here, need to do that now. Be
8030d6f5d97SBram Moolenaar // careful not to go past the NUL.
80481366db6SBram Moolenaar prev_current_col = current_col;
80581366db6SBram Moolenaar if (syn_getcurline()[current_col] != NUL)
806071d4279SBram Moolenaar ++current_col;
807071d4279SBram Moolenaar check_state_ends();
80881366db6SBram Moolenaar current_col = prev_current_col;
809071d4279SBram Moolenaar }
810071d4279SBram Moolenaar else
811071d4279SBram Moolenaar break;
812071d4279SBram Moolenaar }
813071d4279SBram Moolenaar }
814071d4279SBram Moolenaar
815071d4279SBram Moolenaar /*
816071d4279SBram Moolenaar * If a sync point was encountered, break here.
817071d4279SBram Moolenaar */
818071d4279SBram Moolenaar if (found_flags)
819071d4279SBram Moolenaar {
820071d4279SBram Moolenaar /*
821071d4279SBram Moolenaar * Put the item that was specified by the sync point on the
822071d4279SBram Moolenaar * state stack. If there was no item specified, make the
823071d4279SBram Moolenaar * state stack empty.
824071d4279SBram Moolenaar */
825071d4279SBram Moolenaar clear_current_state();
826071d4279SBram Moolenaar if (found_match_idx >= 0
827071d4279SBram Moolenaar && push_current_state(found_match_idx) == OK)
828071d4279SBram Moolenaar update_si_attr(current_state.ga_len - 1);
829071d4279SBram Moolenaar
830071d4279SBram Moolenaar /*
831071d4279SBram Moolenaar * When using "grouphere", continue from the sync point
832071d4279SBram Moolenaar * match, until the end of the line. Parsing starts at
833071d4279SBram Moolenaar * the next line.
834071d4279SBram Moolenaar * For "groupthere" the parsing starts at start_lnum.
835071d4279SBram Moolenaar */
836071d4279SBram Moolenaar if (found_flags & HL_SYNC_HERE)
837071d4279SBram Moolenaar {
838071d4279SBram Moolenaar if (current_state.ga_len)
839071d4279SBram Moolenaar {
840071d4279SBram Moolenaar cur_si = &CUR_STATE(current_state.ga_len - 1);
841071d4279SBram Moolenaar cur_si->si_h_startpos.lnum = found_current_lnum;
842071d4279SBram Moolenaar cur_si->si_h_startpos.col = found_current_col;
843071d4279SBram Moolenaar update_si_end(cur_si, (int)current_col, TRUE);
844071d4279SBram Moolenaar check_keepend();
845071d4279SBram Moolenaar }
846071d4279SBram Moolenaar current_col = found_m_endpos.col;
847071d4279SBram Moolenaar current_lnum = found_m_endpos.lnum;
848071d4279SBram Moolenaar (void)syn_finish_line(FALSE);
849071d4279SBram Moolenaar ++current_lnum;
850071d4279SBram Moolenaar }
851071d4279SBram Moolenaar else
852071d4279SBram Moolenaar current_lnum = start_lnum;
853071d4279SBram Moolenaar
854071d4279SBram Moolenaar break;
855071d4279SBram Moolenaar }
856071d4279SBram Moolenaar
857071d4279SBram Moolenaar end_lnum = lnum;
858071d4279SBram Moolenaar invalidate_current_state();
859071d4279SBram Moolenaar }
860071d4279SBram Moolenaar
8610d6f5d97SBram Moolenaar // Ran into start of the file or exceeded maximum number of lines
862071d4279SBram Moolenaar if (lnum <= break_lnum)
863071d4279SBram Moolenaar {
864071d4279SBram Moolenaar invalidate_current_state();
865071d4279SBram Moolenaar current_lnum = break_lnum + 1;
866071d4279SBram Moolenaar }
867071d4279SBram Moolenaar }
868071d4279SBram Moolenaar
869071d4279SBram Moolenaar validate_current_state();
870071d4279SBram Moolenaar }
871071d4279SBram Moolenaar
872b8060fe8SBram Moolenaar static void
save_chartab(char_u * chartab)873b8060fe8SBram Moolenaar save_chartab(char_u *chartab)
874b8060fe8SBram Moolenaar {
875b8060fe8SBram Moolenaar if (syn_block->b_syn_isk != empty_option)
876b8060fe8SBram Moolenaar {
877b8060fe8SBram Moolenaar mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
878b8060fe8SBram Moolenaar mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
879b8060fe8SBram Moolenaar (size_t)32);
880b8060fe8SBram Moolenaar }
881b8060fe8SBram Moolenaar }
882b8060fe8SBram Moolenaar
883b8060fe8SBram Moolenaar static void
restore_chartab(char_u * chartab)884b8060fe8SBram Moolenaar restore_chartab(char_u *chartab)
885b8060fe8SBram Moolenaar {
886b8060fe8SBram Moolenaar if (syn_win->w_s->b_syn_isk != empty_option)
887b8060fe8SBram Moolenaar mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
888b8060fe8SBram Moolenaar }
889b8060fe8SBram Moolenaar
890071d4279SBram Moolenaar /*
891071d4279SBram Moolenaar * Return TRUE if the line-continuation pattern matches in line "lnum".
892071d4279SBram Moolenaar */
893071d4279SBram Moolenaar static int
syn_match_linecont(linenr_T lnum)894764b23c8SBram Moolenaar syn_match_linecont(linenr_T lnum)
895071d4279SBram Moolenaar {
896071d4279SBram Moolenaar regmmatch_T regmatch;
897dffa5b8eSBram Moolenaar int r;
8980d6f5d97SBram Moolenaar char_u buf_chartab[32]; // chartab array for syn iskyeyword
899071d4279SBram Moolenaar
900860cae1cSBram Moolenaar if (syn_block->b_syn_linecont_prog != NULL)
901071d4279SBram Moolenaar {
9020d6f5d97SBram Moolenaar // use syntax iskeyword option
903b8060fe8SBram Moolenaar save_chartab(buf_chartab);
904860cae1cSBram Moolenaar regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
905860cae1cSBram Moolenaar regmatch.regprog = syn_block->b_syn_linecont_prog;
906dffa5b8eSBram Moolenaar r = syn_regexec(®match, lnum, (colnr_T)0,
9078a7f5a2dSBram Moolenaar IF_SYN_TIME(&syn_block->b_syn_linecont_time));
908dffa5b8eSBram Moolenaar syn_block->b_syn_linecont_prog = regmatch.regprog;
909b8060fe8SBram Moolenaar restore_chartab(buf_chartab);
910dffa5b8eSBram Moolenaar return r;
911071d4279SBram Moolenaar }
912071d4279SBram Moolenaar return FALSE;
913071d4279SBram Moolenaar }
914071d4279SBram Moolenaar
915071d4279SBram Moolenaar /*
916071d4279SBram Moolenaar * Prepare the current state for the start of a line.
917071d4279SBram Moolenaar */
918071d4279SBram Moolenaar static void
syn_start_line(void)919764b23c8SBram Moolenaar syn_start_line(void)
920071d4279SBram Moolenaar {
921071d4279SBram Moolenaar current_finished = FALSE;
922071d4279SBram Moolenaar current_col = 0;
923071d4279SBram Moolenaar
924071d4279SBram Moolenaar /*
925071d4279SBram Moolenaar * Need to update the end of a start/skip/end that continues from the
926071d4279SBram Moolenaar * previous line and regions that have "keepend".
927071d4279SBram Moolenaar */
928071d4279SBram Moolenaar if (current_state.ga_len > 0)
9296fa46363SBram Moolenaar {
930071d4279SBram Moolenaar syn_update_ends(TRUE);
9316fa46363SBram Moolenaar check_state_ends();
9326fa46363SBram Moolenaar }
933071d4279SBram Moolenaar
934071d4279SBram Moolenaar next_match_idx = -1;
935071d4279SBram Moolenaar ++current_line_id;
936ea20de81SBram Moolenaar #ifdef FEAT_CONCEAL
937cc0750dcSBram Moolenaar next_seqnr = 1;
938ea20de81SBram Moolenaar #endif
939071d4279SBram Moolenaar }
940071d4279SBram Moolenaar
941071d4279SBram Moolenaar /*
942071d4279SBram Moolenaar * Check for items in the stack that need their end updated.
943071d4279SBram Moolenaar * When "startofline" is TRUE the last item is always updated.
944071d4279SBram Moolenaar * When "startofline" is FALSE the item with "keepend" is forcefully updated.
945071d4279SBram Moolenaar */
946071d4279SBram Moolenaar static void
syn_update_ends(int startofline)947764b23c8SBram Moolenaar syn_update_ends(int startofline)
948071d4279SBram Moolenaar {
949071d4279SBram Moolenaar stateitem_T *cur_si;
950071d4279SBram Moolenaar int i;
9517bd2cd8dSBram Moolenaar int seen_keepend;
952071d4279SBram Moolenaar
953071d4279SBram Moolenaar if (startofline)
954071d4279SBram Moolenaar {
9550d6f5d97SBram Moolenaar // Check for a match carried over from a previous line with a
9560d6f5d97SBram Moolenaar // contained region. The match ends as soon as the region ends.
957071d4279SBram Moolenaar for (i = 0; i < current_state.ga_len; ++i)
958071d4279SBram Moolenaar {
959071d4279SBram Moolenaar cur_si = &CUR_STATE(i);
960071d4279SBram Moolenaar if (cur_si->si_idx >= 0
961860cae1cSBram Moolenaar && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
962071d4279SBram Moolenaar == SPTYPE_MATCH
963071d4279SBram Moolenaar && cur_si->si_m_endpos.lnum < current_lnum)
964071d4279SBram Moolenaar {
965071d4279SBram Moolenaar cur_si->si_flags |= HL_MATCHCONT;
966071d4279SBram Moolenaar cur_si->si_m_endpos.lnum = 0;
967071d4279SBram Moolenaar cur_si->si_m_endpos.col = 0;
968071d4279SBram Moolenaar cur_si->si_h_endpos = cur_si->si_m_endpos;
969071d4279SBram Moolenaar cur_si->si_ends = TRUE;
970071d4279SBram Moolenaar }
971071d4279SBram Moolenaar }
972071d4279SBram Moolenaar }
973071d4279SBram Moolenaar
974071d4279SBram Moolenaar /*
975071d4279SBram Moolenaar * Need to update the end of a start/skip/end that continues from the
976071d4279SBram Moolenaar * previous line. And regions that have "keepend", because they may
9777bd2cd8dSBram Moolenaar * influence contained items. If we've just removed "extend"
9787bd2cd8dSBram Moolenaar * (startofline == 0) then we should update ends of normal regions
9797bd2cd8dSBram Moolenaar * contained inside "keepend" because "extend" could have extended
9807bd2cd8dSBram Moolenaar * these "keepend" regions as well as contained normal regions.
981071d4279SBram Moolenaar * Then check for items ending in column 0.
982071d4279SBram Moolenaar */
983071d4279SBram Moolenaar i = current_state.ga_len - 1;
984071d4279SBram Moolenaar if (keepend_level >= 0)
985071d4279SBram Moolenaar for ( ; i > keepend_level; --i)
986071d4279SBram Moolenaar if (CUR_STATE(i).si_flags & HL_EXTEND)
987071d4279SBram Moolenaar break;
9887bd2cd8dSBram Moolenaar
9897bd2cd8dSBram Moolenaar seen_keepend = FALSE;
990071d4279SBram Moolenaar for ( ; i < current_state.ga_len; ++i)
991071d4279SBram Moolenaar {
992071d4279SBram Moolenaar cur_si = &CUR_STATE(i);
993071d4279SBram Moolenaar if ((cur_si->si_flags & HL_KEEPEND)
9947bd2cd8dSBram Moolenaar || (seen_keepend && !startofline)
995071d4279SBram Moolenaar || (i == current_state.ga_len - 1 && startofline))
996071d4279SBram Moolenaar {
9970d6f5d97SBram Moolenaar cur_si->si_h_startpos.col = 0; // start highl. in col 0
998071d4279SBram Moolenaar cur_si->si_h_startpos.lnum = current_lnum;
999071d4279SBram Moolenaar
1000071d4279SBram Moolenaar if (!(cur_si->si_flags & HL_MATCHCONT))
1001071d4279SBram Moolenaar update_si_end(cur_si, (int)current_col, !startofline);
10027bd2cd8dSBram Moolenaar
10037bd2cd8dSBram Moolenaar if (!startofline && (cur_si->si_flags & HL_KEEPEND))
10047bd2cd8dSBram Moolenaar seen_keepend = TRUE;
1005071d4279SBram Moolenaar }
1006071d4279SBram Moolenaar }
1007071d4279SBram Moolenaar check_keepend();
1008071d4279SBram Moolenaar }
1009071d4279SBram Moolenaar
10100d6f5d97SBram Moolenaar /////////////////////////////////////////
10110d6f5d97SBram Moolenaar // Handling of the state stack cache.
1012071d4279SBram Moolenaar
1013071d4279SBram Moolenaar /*
1014071d4279SBram Moolenaar * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1015071d4279SBram Moolenaar *
1016071d4279SBram Moolenaar * To speed up syntax highlighting, the state stack for the start of some
1017071d4279SBram Moolenaar * lines is cached. These entries can be used to start parsing at that point.
1018071d4279SBram Moolenaar *
1019071d4279SBram Moolenaar * The stack is kept in b_sst_array[] for each buffer. There is a list of
1020071d4279SBram Moolenaar * valid entries. b_sst_first points to the first one, then follow sst_next.
1021071d4279SBram Moolenaar * The entries are sorted on line number. The first entry is often for line 2
1022071d4279SBram Moolenaar * (line 1 always starts with an empty stack).
1023071d4279SBram Moolenaar * There is also a list for free entries. This construction is used to avoid
1024071d4279SBram Moolenaar * having to allocate and free memory blocks too often.
1025071d4279SBram Moolenaar *
1026071d4279SBram Moolenaar * When making changes to the buffer, this is logged in b_mod_*. When calling
1027071d4279SBram Moolenaar * update_screen() to update the display, it will call
1028071d4279SBram Moolenaar * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1029071d4279SBram Moolenaar * entries. The entries which are inside the changed area are removed,
1030071d4279SBram Moolenaar * because they must be recomputed. Entries below the changed have their line
1031071d4279SBram Moolenaar * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1032071d4279SBram Moolenaar * set to indicate that a check must be made if the changed lines would change
1033071d4279SBram Moolenaar * the cached entry.
1034071d4279SBram Moolenaar *
1035071d4279SBram Moolenaar * When later displaying lines, an entry is stored for each line. Displayed
1036071d4279SBram Moolenaar * lines are likely to be displayed again, in which case the state at the
1037071d4279SBram Moolenaar * start of the line is needed.
1038071d4279SBram Moolenaar * For not displayed lines, an entry is stored for every so many lines. These
1039071d4279SBram Moolenaar * entries will be used e.g., when scrolling backwards. The distance between
1040071d4279SBram Moolenaar * entries depends on the number of lines in the buffer. For small buffers
1041071d4279SBram Moolenaar * the distance is fixed at SST_DIST, for large buffers there is a fixed
1042071d4279SBram Moolenaar * number of entries SST_MAX_ENTRIES, and the distance is computed.
1043071d4279SBram Moolenaar */
1044071d4279SBram Moolenaar
1045860cae1cSBram Moolenaar static void
syn_stack_free_block(synblock_T * block)1046764b23c8SBram Moolenaar syn_stack_free_block(synblock_T *block)
1047860cae1cSBram Moolenaar {
1048860cae1cSBram Moolenaar synstate_T *p;
1049860cae1cSBram Moolenaar
1050860cae1cSBram Moolenaar if (block->b_sst_array != NULL)
1051860cae1cSBram Moolenaar {
105200d253e2SBram Moolenaar FOR_ALL_SYNSTATES(block, p)
1053860cae1cSBram Moolenaar clear_syn_state(p);
1054d23a8236SBram Moolenaar VIM_CLEAR(block->b_sst_array);
105595892c27SBram Moolenaar block->b_sst_first = NULL;
1056860cae1cSBram Moolenaar block->b_sst_len = 0;
1057860cae1cSBram Moolenaar }
1058860cae1cSBram Moolenaar }
1059071d4279SBram Moolenaar /*
1060071d4279SBram Moolenaar * Free b_sst_array[] for buffer "buf".
1061071d4279SBram Moolenaar * Used when syntax items changed to force resyncing everywhere.
1062071d4279SBram Moolenaar */
1063071d4279SBram Moolenaar void
syn_stack_free_all(synblock_T * block)1064764b23c8SBram Moolenaar syn_stack_free_all(synblock_T *block)
1065071d4279SBram Moolenaar {
1066a6c07603SBram Moolenaar #ifdef FEAT_FOLDING
1067071d4279SBram Moolenaar win_T *wp;
1068a6c07603SBram Moolenaar #endif
1069071d4279SBram Moolenaar
1070860cae1cSBram Moolenaar syn_stack_free_block(block);
1071860cae1cSBram Moolenaar
1072071d4279SBram Moolenaar #ifdef FEAT_FOLDING
10730d6f5d97SBram Moolenaar // When using "syntax" fold method, must update all folds.
1074071d4279SBram Moolenaar FOR_ALL_WINDOWS(wp)
1075071d4279SBram Moolenaar {
1076860cae1cSBram Moolenaar if (wp->w_s == block && foldmethodIsSyntax(wp))
1077071d4279SBram Moolenaar foldUpdateAll(wp);
1078071d4279SBram Moolenaar }
1079071d4279SBram Moolenaar #endif
1080071d4279SBram Moolenaar }
1081071d4279SBram Moolenaar
1082071d4279SBram Moolenaar /*
1083071d4279SBram Moolenaar * Allocate the syntax state stack for syn_buf when needed.
1084071d4279SBram Moolenaar * If the number of entries in b_sst_array[] is much too big or a bit too
1085071d4279SBram Moolenaar * small, reallocate it.
1086071d4279SBram Moolenaar * Also used to allocate b_sst_array[] for the first time.
1087071d4279SBram Moolenaar */
1088071d4279SBram Moolenaar static void
syn_stack_alloc(void)1089764b23c8SBram Moolenaar syn_stack_alloc(void)
1090071d4279SBram Moolenaar {
1091071d4279SBram Moolenaar long len;
1092071d4279SBram Moolenaar synstate_T *to, *from;
1093071d4279SBram Moolenaar synstate_T *sstp;
1094071d4279SBram Moolenaar
1095071d4279SBram Moolenaar len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1096071d4279SBram Moolenaar if (len < SST_MIN_ENTRIES)
1097071d4279SBram Moolenaar len = SST_MIN_ENTRIES;
1098071d4279SBram Moolenaar else if (len > SST_MAX_ENTRIES)
1099071d4279SBram Moolenaar len = SST_MAX_ENTRIES;
1100860cae1cSBram Moolenaar if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
1101071d4279SBram Moolenaar {
11020d6f5d97SBram Moolenaar // Allocate 50% too much, to avoid reallocating too often.
1103071d4279SBram Moolenaar len = syn_buf->b_ml.ml_line_count;
1104071d4279SBram Moolenaar len = (len + len / 2) / SST_DIST + Rows * 2;
1105071d4279SBram Moolenaar if (len < SST_MIN_ENTRIES)
1106071d4279SBram Moolenaar len = SST_MIN_ENTRIES;
1107071d4279SBram Moolenaar else if (len > SST_MAX_ENTRIES)
1108071d4279SBram Moolenaar len = SST_MAX_ENTRIES;
1109071d4279SBram Moolenaar
1110860cae1cSBram Moolenaar if (syn_block->b_sst_array != NULL)
1111071d4279SBram Moolenaar {
11120d6f5d97SBram Moolenaar // When shrinking the array, cleanup the existing stack.
11130d6f5d97SBram Moolenaar // Make sure that all valid entries fit in the new array.
1114860cae1cSBram Moolenaar while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
1115071d4279SBram Moolenaar && syn_stack_cleanup())
1116071d4279SBram Moolenaar ;
1117860cae1cSBram Moolenaar if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1118860cae1cSBram Moolenaar len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
1119071d4279SBram Moolenaar }
1120071d4279SBram Moolenaar
1121c799fe20SBram Moolenaar sstp = ALLOC_CLEAR_MULT(synstate_T, len);
11220d6f5d97SBram Moolenaar if (sstp == NULL) // out of memory!
1123071d4279SBram Moolenaar return;
1124071d4279SBram Moolenaar
1125071d4279SBram Moolenaar to = sstp - 1;
1126860cae1cSBram Moolenaar if (syn_block->b_sst_array != NULL)
1127071d4279SBram Moolenaar {
11280d6f5d97SBram Moolenaar // Move the states from the old array to the new one.
1129860cae1cSBram Moolenaar for (from = syn_block->b_sst_first; from != NULL;
1130071d4279SBram Moolenaar from = from->sst_next)
1131071d4279SBram Moolenaar {
1132071d4279SBram Moolenaar ++to;
1133071d4279SBram Moolenaar *to = *from;
1134071d4279SBram Moolenaar to->sst_next = to + 1;
1135071d4279SBram Moolenaar }
1136071d4279SBram Moolenaar }
1137071d4279SBram Moolenaar if (to != sstp - 1)
1138071d4279SBram Moolenaar {
1139071d4279SBram Moolenaar to->sst_next = NULL;
1140860cae1cSBram Moolenaar syn_block->b_sst_first = sstp;
1141860cae1cSBram Moolenaar syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
1142071d4279SBram Moolenaar }
1143071d4279SBram Moolenaar else
1144071d4279SBram Moolenaar {
1145860cae1cSBram Moolenaar syn_block->b_sst_first = NULL;
1146860cae1cSBram Moolenaar syn_block->b_sst_freecount = len;
1147071d4279SBram Moolenaar }
1148071d4279SBram Moolenaar
11490d6f5d97SBram Moolenaar // Create the list of free entries.
1150860cae1cSBram Moolenaar syn_block->b_sst_firstfree = to + 1;
1151071d4279SBram Moolenaar while (++to < sstp + len)
1152071d4279SBram Moolenaar to->sst_next = to + 1;
1153071d4279SBram Moolenaar (sstp + len - 1)->sst_next = NULL;
1154071d4279SBram Moolenaar
1155860cae1cSBram Moolenaar vim_free(syn_block->b_sst_array);
1156860cae1cSBram Moolenaar syn_block->b_sst_array = sstp;
1157860cae1cSBram Moolenaar syn_block->b_sst_len = len;
1158071d4279SBram Moolenaar }
1159071d4279SBram Moolenaar }
1160071d4279SBram Moolenaar
1161071d4279SBram Moolenaar /*
1162071d4279SBram Moolenaar * Check for changes in a buffer to affect stored syntax states. Uses the
1163071d4279SBram Moolenaar * b_mod_* fields.
1164071d4279SBram Moolenaar * Called from update_screen(), before screen is being updated, once for each
1165071d4279SBram Moolenaar * displayed buffer.
1166071d4279SBram Moolenaar */
1167071d4279SBram Moolenaar void
syn_stack_apply_changes(buf_T * buf)1168764b23c8SBram Moolenaar syn_stack_apply_changes(buf_T *buf)
1169071d4279SBram Moolenaar {
1170860cae1cSBram Moolenaar win_T *wp;
1171860cae1cSBram Moolenaar
1172860cae1cSBram Moolenaar syn_stack_apply_changes_block(&buf->b_s, buf);
1173860cae1cSBram Moolenaar
1174860cae1cSBram Moolenaar FOR_ALL_WINDOWS(wp)
1175860cae1cSBram Moolenaar {
1176860cae1cSBram Moolenaar if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1177860cae1cSBram Moolenaar syn_stack_apply_changes_block(wp->w_s, buf);
1178860cae1cSBram Moolenaar }
1179860cae1cSBram Moolenaar }
1180860cae1cSBram Moolenaar
1181860cae1cSBram Moolenaar static void
syn_stack_apply_changes_block(synblock_T * block,buf_T * buf)1182764b23c8SBram Moolenaar syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
1183860cae1cSBram Moolenaar {
1184071d4279SBram Moolenaar synstate_T *p, *prev, *np;
1185071d4279SBram Moolenaar linenr_T n;
1186071d4279SBram Moolenaar
1187071d4279SBram Moolenaar prev = NULL;
1188860cae1cSBram Moolenaar for (p = block->b_sst_first; p != NULL; )
1189071d4279SBram Moolenaar {
1190860cae1cSBram Moolenaar if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
1191071d4279SBram Moolenaar {
1192071d4279SBram Moolenaar n = p->sst_lnum + buf->b_mod_xlines;
1193071d4279SBram Moolenaar if (n <= buf->b_mod_bot)
1194071d4279SBram Moolenaar {
11950d6f5d97SBram Moolenaar // this state is inside the changed area, remove it
1196071d4279SBram Moolenaar np = p->sst_next;
1197071d4279SBram Moolenaar if (prev == NULL)
1198860cae1cSBram Moolenaar block->b_sst_first = np;
1199071d4279SBram Moolenaar else
1200071d4279SBram Moolenaar prev->sst_next = np;
1201860cae1cSBram Moolenaar syn_stack_free_entry(block, p);
1202071d4279SBram Moolenaar p = np;
1203071d4279SBram Moolenaar continue;
1204071d4279SBram Moolenaar }
12050d6f5d97SBram Moolenaar // This state is below the changed area. Remember the line
12060d6f5d97SBram Moolenaar // that needs to be parsed before this entry can be made valid
12070d6f5d97SBram Moolenaar // again.
1208071d4279SBram Moolenaar if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1209071d4279SBram Moolenaar {
1210071d4279SBram Moolenaar if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1211071d4279SBram Moolenaar p->sst_change_lnum += buf->b_mod_xlines;
1212071d4279SBram Moolenaar else
1213071d4279SBram Moolenaar p->sst_change_lnum = buf->b_mod_top;
1214071d4279SBram Moolenaar }
1215071d4279SBram Moolenaar if (p->sst_change_lnum == 0
1216071d4279SBram Moolenaar || p->sst_change_lnum < buf->b_mod_bot)
1217071d4279SBram Moolenaar p->sst_change_lnum = buf->b_mod_bot;
1218071d4279SBram Moolenaar
1219071d4279SBram Moolenaar p->sst_lnum = n;
1220071d4279SBram Moolenaar }
1221071d4279SBram Moolenaar prev = p;
1222071d4279SBram Moolenaar p = p->sst_next;
1223071d4279SBram Moolenaar }
1224071d4279SBram Moolenaar }
1225071d4279SBram Moolenaar
1226071d4279SBram Moolenaar /*
1227071d4279SBram Moolenaar * Reduce the number of entries in the state stack for syn_buf.
1228071d4279SBram Moolenaar * Returns TRUE if at least one entry was freed.
1229071d4279SBram Moolenaar */
1230071d4279SBram Moolenaar static int
syn_stack_cleanup(void)1231764b23c8SBram Moolenaar syn_stack_cleanup(void)
1232071d4279SBram Moolenaar {
1233071d4279SBram Moolenaar synstate_T *p, *prev;
1234071d4279SBram Moolenaar disptick_T tick;
1235071d4279SBram Moolenaar int above;
1236071d4279SBram Moolenaar int dist;
1237071d4279SBram Moolenaar int retval = FALSE;
1238071d4279SBram Moolenaar
123995892c27SBram Moolenaar if (syn_block->b_sst_first == NULL)
1240071d4279SBram Moolenaar return retval;
1241071d4279SBram Moolenaar
12420d6f5d97SBram Moolenaar // Compute normal distance between non-displayed entries.
1243860cae1cSBram Moolenaar if (syn_block->b_sst_len <= Rows)
1244910f66f9SBram Moolenaar dist = 999999;
1245910f66f9SBram Moolenaar else
1246860cae1cSBram Moolenaar dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
1247071d4279SBram Moolenaar
1248071d4279SBram Moolenaar /*
1249f5b6386fSBram Moolenaar * Go through the list to find the "tick" for the oldest entry that can
1250071d4279SBram Moolenaar * be removed. Set "above" when the "tick" for the oldest entry is above
1251071d4279SBram Moolenaar * "b_sst_lasttick" (the display tick wraps around).
1252071d4279SBram Moolenaar */
1253860cae1cSBram Moolenaar tick = syn_block->b_sst_lasttick;
1254071d4279SBram Moolenaar above = FALSE;
1255860cae1cSBram Moolenaar prev = syn_block->b_sst_first;
1256071d4279SBram Moolenaar for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1257071d4279SBram Moolenaar {
1258071d4279SBram Moolenaar if (prev->sst_lnum + dist > p->sst_lnum)
1259071d4279SBram Moolenaar {
1260860cae1cSBram Moolenaar if (p->sst_tick > syn_block->b_sst_lasttick)
1261071d4279SBram Moolenaar {
1262071d4279SBram Moolenaar if (!above || p->sst_tick < tick)
1263071d4279SBram Moolenaar tick = p->sst_tick;
1264071d4279SBram Moolenaar above = TRUE;
1265071d4279SBram Moolenaar }
1266071d4279SBram Moolenaar else if (!above && p->sst_tick < tick)
1267071d4279SBram Moolenaar tick = p->sst_tick;
1268071d4279SBram Moolenaar }
1269071d4279SBram Moolenaar }
1270071d4279SBram Moolenaar
1271071d4279SBram Moolenaar /*
1272071d4279SBram Moolenaar * Go through the list to make the entries for the oldest tick at an
1273071d4279SBram Moolenaar * interval of several lines.
1274071d4279SBram Moolenaar */
1275860cae1cSBram Moolenaar prev = syn_block->b_sst_first;
1276071d4279SBram Moolenaar for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1277071d4279SBram Moolenaar {
1278071d4279SBram Moolenaar if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1279071d4279SBram Moolenaar {
12800d6f5d97SBram Moolenaar // Move this entry from used list to free list
1281071d4279SBram Moolenaar prev->sst_next = p->sst_next;
1282860cae1cSBram Moolenaar syn_stack_free_entry(syn_block, p);
1283071d4279SBram Moolenaar p = prev;
1284071d4279SBram Moolenaar retval = TRUE;
1285071d4279SBram Moolenaar }
1286071d4279SBram Moolenaar }
1287071d4279SBram Moolenaar return retval;
1288071d4279SBram Moolenaar }
1289071d4279SBram Moolenaar
1290071d4279SBram Moolenaar /*
1291071d4279SBram Moolenaar * Free the allocated memory for a syn_state item.
1292071d4279SBram Moolenaar * Move the entry into the free list.
1293071d4279SBram Moolenaar */
1294071d4279SBram Moolenaar static void
syn_stack_free_entry(synblock_T * block,synstate_T * p)1295764b23c8SBram Moolenaar syn_stack_free_entry(synblock_T *block, synstate_T *p)
1296071d4279SBram Moolenaar {
1297071d4279SBram Moolenaar clear_syn_state(p);
1298860cae1cSBram Moolenaar p->sst_next = block->b_sst_firstfree;
1299860cae1cSBram Moolenaar block->b_sst_firstfree = p;
1300860cae1cSBram Moolenaar ++block->b_sst_freecount;
1301071d4279SBram Moolenaar }
1302071d4279SBram Moolenaar
1303071d4279SBram Moolenaar /*
1304071d4279SBram Moolenaar * Find an entry in the list of state stacks at or before "lnum".
1305071d4279SBram Moolenaar * Returns NULL when there is no entry or the first entry is after "lnum".
1306071d4279SBram Moolenaar */
1307071d4279SBram Moolenaar static synstate_T *
syn_stack_find_entry(linenr_T lnum)1308764b23c8SBram Moolenaar syn_stack_find_entry(linenr_T lnum)
1309071d4279SBram Moolenaar {
1310071d4279SBram Moolenaar synstate_T *p, *prev;
1311071d4279SBram Moolenaar
1312071d4279SBram Moolenaar prev = NULL;
1313860cae1cSBram Moolenaar for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
1314071d4279SBram Moolenaar {
1315071d4279SBram Moolenaar if (p->sst_lnum == lnum)
1316071d4279SBram Moolenaar return p;
1317071d4279SBram Moolenaar if (p->sst_lnum > lnum)
1318071d4279SBram Moolenaar break;
1319071d4279SBram Moolenaar }
1320071d4279SBram Moolenaar return prev;
1321071d4279SBram Moolenaar }
1322071d4279SBram Moolenaar
1323071d4279SBram Moolenaar /*
1324071d4279SBram Moolenaar * Try saving the current state in b_sst_array[].
1325071d4279SBram Moolenaar * The current state must be valid for the start of the current_lnum line!
1326071d4279SBram Moolenaar */
1327071d4279SBram Moolenaar static synstate_T *
store_current_state(void)1328764b23c8SBram Moolenaar store_current_state(void)
1329071d4279SBram Moolenaar {
1330071d4279SBram Moolenaar int i;
1331071d4279SBram Moolenaar synstate_T *p;
1332071d4279SBram Moolenaar bufstate_T *bp;
1333071d4279SBram Moolenaar stateitem_T *cur_si;
1334dbe31750SBram Moolenaar synstate_T *sp = syn_stack_find_entry(current_lnum);
1335071d4279SBram Moolenaar
1336071d4279SBram Moolenaar /*
1337071d4279SBram Moolenaar * If the current state contains a start or end pattern that continues
1338071d4279SBram Moolenaar * from the previous line, we can't use it. Don't store it then.
1339071d4279SBram Moolenaar */
1340071d4279SBram Moolenaar for (i = current_state.ga_len - 1; i >= 0; --i)
1341071d4279SBram Moolenaar {
1342071d4279SBram Moolenaar cur_si = &CUR_STATE(i);
1343071d4279SBram Moolenaar if (cur_si->si_h_startpos.lnum >= current_lnum
1344071d4279SBram Moolenaar || cur_si->si_m_endpos.lnum >= current_lnum
1345071d4279SBram Moolenaar || cur_si->si_h_endpos.lnum >= current_lnum
1346071d4279SBram Moolenaar || (cur_si->si_end_idx
1347071d4279SBram Moolenaar && cur_si->si_eoe_pos.lnum >= current_lnum))
1348071d4279SBram Moolenaar break;
1349071d4279SBram Moolenaar }
1350071d4279SBram Moolenaar if (i >= 0)
1351071d4279SBram Moolenaar {
1352071d4279SBram Moolenaar if (sp != NULL)
1353071d4279SBram Moolenaar {
13540d6f5d97SBram Moolenaar // find "sp" in the list and remove it
1355860cae1cSBram Moolenaar if (syn_block->b_sst_first == sp)
13560d6f5d97SBram Moolenaar // it's the first entry
1357860cae1cSBram Moolenaar syn_block->b_sst_first = sp->sst_next;
1358071d4279SBram Moolenaar else
1359071d4279SBram Moolenaar {
13600d6f5d97SBram Moolenaar // find the entry just before this one to adjust sst_next
136100d253e2SBram Moolenaar FOR_ALL_SYNSTATES(syn_block, p)
1362071d4279SBram Moolenaar if (p->sst_next == sp)
1363071d4279SBram Moolenaar break;
13640d6f5d97SBram Moolenaar if (p != NULL) // just in case
1365071d4279SBram Moolenaar p->sst_next = sp->sst_next;
1366071d4279SBram Moolenaar }
1367860cae1cSBram Moolenaar syn_stack_free_entry(syn_block, sp);
1368071d4279SBram Moolenaar sp = NULL;
1369071d4279SBram Moolenaar }
1370071d4279SBram Moolenaar }
1371071d4279SBram Moolenaar else if (sp == NULL || sp->sst_lnum != current_lnum)
1372071d4279SBram Moolenaar {
1373071d4279SBram Moolenaar /*
1374071d4279SBram Moolenaar * Add a new entry
1375071d4279SBram Moolenaar */
13760d6f5d97SBram Moolenaar // If no free items, cleanup the array first.
1377860cae1cSBram Moolenaar if (syn_block->b_sst_freecount == 0)
1378071d4279SBram Moolenaar {
1379071d4279SBram Moolenaar (void)syn_stack_cleanup();
13800d6f5d97SBram Moolenaar // "sp" may have been moved to the freelist now
1381071d4279SBram Moolenaar sp = syn_stack_find_entry(current_lnum);
1382071d4279SBram Moolenaar }
13830d6f5d97SBram Moolenaar // Still no free items? Must be a strange problem...
1384860cae1cSBram Moolenaar if (syn_block->b_sst_freecount == 0)
1385071d4279SBram Moolenaar sp = NULL;
1386071d4279SBram Moolenaar else
1387071d4279SBram Moolenaar {
13880d6f5d97SBram Moolenaar // Take the first item from the free list and put it in the used
13890d6f5d97SBram Moolenaar // list, after *sp
1390860cae1cSBram Moolenaar p = syn_block->b_sst_firstfree;
1391860cae1cSBram Moolenaar syn_block->b_sst_firstfree = p->sst_next;
1392860cae1cSBram Moolenaar --syn_block->b_sst_freecount;
1393071d4279SBram Moolenaar if (sp == NULL)
1394071d4279SBram Moolenaar {
13950d6f5d97SBram Moolenaar // Insert in front of the list
1396860cae1cSBram Moolenaar p->sst_next = syn_block->b_sst_first;
1397860cae1cSBram Moolenaar syn_block->b_sst_first = p;
1398071d4279SBram Moolenaar }
1399071d4279SBram Moolenaar else
1400071d4279SBram Moolenaar {
14010d6f5d97SBram Moolenaar // insert in list after *sp
1402071d4279SBram Moolenaar p->sst_next = sp->sst_next;
1403071d4279SBram Moolenaar sp->sst_next = p;
1404071d4279SBram Moolenaar }
1405071d4279SBram Moolenaar sp = p;
1406071d4279SBram Moolenaar sp->sst_stacksize = 0;
1407071d4279SBram Moolenaar sp->sst_lnum = current_lnum;
1408071d4279SBram Moolenaar }
1409071d4279SBram Moolenaar }
1410071d4279SBram Moolenaar if (sp != NULL)
1411071d4279SBram Moolenaar {
14120d6f5d97SBram Moolenaar // When overwriting an existing state stack, clear it first
1413071d4279SBram Moolenaar clear_syn_state(sp);
1414071d4279SBram Moolenaar sp->sst_stacksize = current_state.ga_len;
1415071d4279SBram Moolenaar if (current_state.ga_len > SST_FIX_STATES)
1416071d4279SBram Moolenaar {
14170d6f5d97SBram Moolenaar // Need to clear it, might be something remaining from when the
14180d6f5d97SBram Moolenaar // length was less than SST_FIX_STATES.
1419071d4279SBram Moolenaar ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1420071d4279SBram Moolenaar if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1421071d4279SBram Moolenaar sp->sst_stacksize = 0;
1422071d4279SBram Moolenaar else
1423071d4279SBram Moolenaar sp->sst_union.sst_ga.ga_len = current_state.ga_len;
1424071d4279SBram Moolenaar bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1425071d4279SBram Moolenaar }
1426071d4279SBram Moolenaar else
1427071d4279SBram Moolenaar bp = sp->sst_union.sst_stack;
1428071d4279SBram Moolenaar for (i = 0; i < sp->sst_stacksize; ++i)
1429071d4279SBram Moolenaar {
1430071d4279SBram Moolenaar bp[i].bs_idx = CUR_STATE(i).si_idx;
1431071d4279SBram Moolenaar bp[i].bs_flags = CUR_STATE(i).si_flags;
14326e202e52SBram Moolenaar #ifdef FEAT_CONCEAL
14336e202e52SBram Moolenaar bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
14346e202e52SBram Moolenaar bp[i].bs_cchar = CUR_STATE(i).si_cchar;
14356e202e52SBram Moolenaar #endif
1436071d4279SBram Moolenaar bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1437071d4279SBram Moolenaar }
1438071d4279SBram Moolenaar sp->sst_next_flags = current_next_flags;
1439071d4279SBram Moolenaar sp->sst_next_list = current_next_list;
1440071d4279SBram Moolenaar sp->sst_tick = display_tick;
1441071d4279SBram Moolenaar sp->sst_change_lnum = 0;
1442071d4279SBram Moolenaar }
1443071d4279SBram Moolenaar current_state_stored = TRUE;
1444071d4279SBram Moolenaar return sp;
1445071d4279SBram Moolenaar }
1446071d4279SBram Moolenaar
1447071d4279SBram Moolenaar /*
1448071d4279SBram Moolenaar * Copy a state stack from "from" in b_sst_array[] to current_state;
1449071d4279SBram Moolenaar */
1450071d4279SBram Moolenaar static void
load_current_state(synstate_T * from)1451764b23c8SBram Moolenaar load_current_state(synstate_T *from)
1452071d4279SBram Moolenaar {
1453071d4279SBram Moolenaar int i;
1454071d4279SBram Moolenaar bufstate_T *bp;
1455071d4279SBram Moolenaar
1456071d4279SBram Moolenaar clear_current_state();
1457071d4279SBram Moolenaar validate_current_state();
1458071d4279SBram Moolenaar keepend_level = -1;
1459071d4279SBram Moolenaar if (from->sst_stacksize
1460071d4279SBram Moolenaar && ga_grow(¤t_state, from->sst_stacksize) != FAIL)
1461071d4279SBram Moolenaar {
1462071d4279SBram Moolenaar if (from->sst_stacksize > SST_FIX_STATES)
1463071d4279SBram Moolenaar bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1464071d4279SBram Moolenaar else
1465071d4279SBram Moolenaar bp = from->sst_union.sst_stack;
1466071d4279SBram Moolenaar for (i = 0; i < from->sst_stacksize; ++i)
1467071d4279SBram Moolenaar {
1468071d4279SBram Moolenaar CUR_STATE(i).si_idx = bp[i].bs_idx;
1469071d4279SBram Moolenaar CUR_STATE(i).si_flags = bp[i].bs_flags;
14706e202e52SBram Moolenaar #ifdef FEAT_CONCEAL
14716e202e52SBram Moolenaar CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
14726e202e52SBram Moolenaar CUR_STATE(i).si_cchar = bp[i].bs_cchar;
14736e202e52SBram Moolenaar #endif
1474071d4279SBram Moolenaar CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1475071d4279SBram Moolenaar if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1476071d4279SBram Moolenaar keepend_level = i;
1477071d4279SBram Moolenaar CUR_STATE(i).si_ends = FALSE;
1478071d4279SBram Moolenaar CUR_STATE(i).si_m_lnum = 0;
1479071d4279SBram Moolenaar if (CUR_STATE(i).si_idx >= 0)
1480071d4279SBram Moolenaar CUR_STATE(i).si_next_list =
1481860cae1cSBram Moolenaar (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
1482071d4279SBram Moolenaar else
1483071d4279SBram Moolenaar CUR_STATE(i).si_next_list = NULL;
1484071d4279SBram Moolenaar update_si_attr(i);
1485071d4279SBram Moolenaar }
1486071d4279SBram Moolenaar current_state.ga_len = from->sst_stacksize;
1487071d4279SBram Moolenaar }
1488071d4279SBram Moolenaar current_next_list = from->sst_next_list;
1489071d4279SBram Moolenaar current_next_flags = from->sst_next_flags;
1490071d4279SBram Moolenaar current_lnum = from->sst_lnum;
1491071d4279SBram Moolenaar }
1492071d4279SBram Moolenaar
1493071d4279SBram Moolenaar /*
1494071d4279SBram Moolenaar * Compare saved state stack "*sp" with the current state.
1495071d4279SBram Moolenaar * Return TRUE when they are equal.
1496071d4279SBram Moolenaar */
1497071d4279SBram Moolenaar static int
syn_stack_equal(synstate_T * sp)1498764b23c8SBram Moolenaar syn_stack_equal(synstate_T *sp)
1499071d4279SBram Moolenaar {
1500071d4279SBram Moolenaar int i, j;
1501071d4279SBram Moolenaar bufstate_T *bp;
1502071d4279SBram Moolenaar reg_extmatch_T *six, *bsx;
1503071d4279SBram Moolenaar
15040d6f5d97SBram Moolenaar // First a quick check if the stacks have the same size end nextlist.
1505071d4279SBram Moolenaar if (sp->sst_stacksize == current_state.ga_len
1506071d4279SBram Moolenaar && sp->sst_next_list == current_next_list)
1507071d4279SBram Moolenaar {
15080d6f5d97SBram Moolenaar // Need to compare all states on both stacks.
1509071d4279SBram Moolenaar if (sp->sst_stacksize > SST_FIX_STATES)
1510071d4279SBram Moolenaar bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1511071d4279SBram Moolenaar else
1512071d4279SBram Moolenaar bp = sp->sst_union.sst_stack;
1513071d4279SBram Moolenaar
1514071d4279SBram Moolenaar for (i = current_state.ga_len; --i >= 0; )
1515071d4279SBram Moolenaar {
15160d6f5d97SBram Moolenaar // If the item has another index the state is different.
1517071d4279SBram Moolenaar if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1518071d4279SBram Moolenaar break;
1519071d4279SBram Moolenaar if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1520071d4279SBram Moolenaar {
15210d6f5d97SBram Moolenaar // When the extmatch pointers are different, the strings in
15220d6f5d97SBram Moolenaar // them can still be the same. Check if the extmatch
15230d6f5d97SBram Moolenaar // references are equal.
1524071d4279SBram Moolenaar bsx = bp[i].bs_extmatch;
1525071d4279SBram Moolenaar six = CUR_STATE(i).si_extmatch;
15260d6f5d97SBram Moolenaar // If one of the extmatch pointers is NULL the states are
15270d6f5d97SBram Moolenaar // different.
1528071d4279SBram Moolenaar if (bsx == NULL || six == NULL)
1529071d4279SBram Moolenaar break;
1530071d4279SBram Moolenaar for (j = 0; j < NSUBEXP; ++j)
1531071d4279SBram Moolenaar {
15320d6f5d97SBram Moolenaar // Check each referenced match string. They must all be
15330d6f5d97SBram Moolenaar // equal.
1534071d4279SBram Moolenaar if (bsx->matches[j] != six->matches[j])
1535071d4279SBram Moolenaar {
15360d6f5d97SBram Moolenaar // If the pointer is different it can still be the
15370d6f5d97SBram Moolenaar // same text. Compare the strings, ignore case when
15380d6f5d97SBram Moolenaar // the start item has the sp_ic flag set.
1539071d4279SBram Moolenaar if (bsx->matches[j] == NULL
1540071d4279SBram Moolenaar || six->matches[j] == NULL)
1541071d4279SBram Moolenaar break;
1542860cae1cSBram Moolenaar if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
1543071d4279SBram Moolenaar ? MB_STRICMP(bsx->matches[j],
1544071d4279SBram Moolenaar six->matches[j]) != 0
1545071d4279SBram Moolenaar : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1546071d4279SBram Moolenaar break;
1547071d4279SBram Moolenaar }
1548071d4279SBram Moolenaar }
1549071d4279SBram Moolenaar if (j != NSUBEXP)
1550071d4279SBram Moolenaar break;
1551071d4279SBram Moolenaar }
1552071d4279SBram Moolenaar }
1553071d4279SBram Moolenaar if (i < 0)
1554071d4279SBram Moolenaar return TRUE;
1555071d4279SBram Moolenaar }
1556071d4279SBram Moolenaar return FALSE;
1557071d4279SBram Moolenaar }
1558071d4279SBram Moolenaar
1559071d4279SBram Moolenaar /*
1560071d4279SBram Moolenaar * We stop parsing syntax above line "lnum". If the stored state at or below
1561071d4279SBram Moolenaar * this line depended on a change before it, it now depends on the line below
1562071d4279SBram Moolenaar * the last parsed line.
1563071d4279SBram Moolenaar * The window looks like this:
1564071d4279SBram Moolenaar * line which changed
1565071d4279SBram Moolenaar * displayed line
1566071d4279SBram Moolenaar * displayed line
1567071d4279SBram Moolenaar * lnum -> line below window
1568071d4279SBram Moolenaar */
1569071d4279SBram Moolenaar void
syntax_end_parsing(linenr_T lnum)1570764b23c8SBram Moolenaar syntax_end_parsing(linenr_T lnum)
1571071d4279SBram Moolenaar {
1572071d4279SBram Moolenaar synstate_T *sp;
1573071d4279SBram Moolenaar
1574071d4279SBram Moolenaar sp = syn_stack_find_entry(lnum);
1575071d4279SBram Moolenaar if (sp != NULL && sp->sst_lnum < lnum)
1576071d4279SBram Moolenaar sp = sp->sst_next;
1577071d4279SBram Moolenaar
1578071d4279SBram Moolenaar if (sp != NULL && sp->sst_change_lnum != 0)
1579071d4279SBram Moolenaar sp->sst_change_lnum = lnum;
1580071d4279SBram Moolenaar }
1581071d4279SBram Moolenaar
1582071d4279SBram Moolenaar /*
1583071d4279SBram Moolenaar * End of handling of the state stack.
1584071d4279SBram Moolenaar ****************************************/
1585071d4279SBram Moolenaar
1586071d4279SBram Moolenaar static void
invalidate_current_state(void)1587764b23c8SBram Moolenaar invalidate_current_state(void)
1588071d4279SBram Moolenaar {
1589071d4279SBram Moolenaar clear_current_state();
15900d6f5d97SBram Moolenaar current_state.ga_itemsize = 0; // mark current_state invalid
1591071d4279SBram Moolenaar current_next_list = NULL;
1592071d4279SBram Moolenaar keepend_level = -1;
1593071d4279SBram Moolenaar }
1594071d4279SBram Moolenaar
1595071d4279SBram Moolenaar static void
validate_current_state(void)1596764b23c8SBram Moolenaar validate_current_state(void)
1597071d4279SBram Moolenaar {
1598071d4279SBram Moolenaar current_state.ga_itemsize = sizeof(stateitem_T);
1599071d4279SBram Moolenaar current_state.ga_growsize = 3;
1600071d4279SBram Moolenaar }
1601071d4279SBram Moolenaar
1602071d4279SBram Moolenaar /*
1603071d4279SBram Moolenaar * Return TRUE if the syntax at start of lnum changed since last time.
1604071d4279SBram Moolenaar * This will only be called just after get_syntax_attr() for the previous
1605071d4279SBram Moolenaar * line, to check if the next line needs to be redrawn too.
1606071d4279SBram Moolenaar */
1607071d4279SBram Moolenaar int
syntax_check_changed(linenr_T lnum)1608764b23c8SBram Moolenaar syntax_check_changed(linenr_T lnum)
1609071d4279SBram Moolenaar {
1610071d4279SBram Moolenaar int retval = TRUE;
1611071d4279SBram Moolenaar synstate_T *sp;
1612071d4279SBram Moolenaar
1613071d4279SBram Moolenaar /*
1614071d4279SBram Moolenaar * Check the state stack when:
1615071d4279SBram Moolenaar * - lnum is just below the previously syntaxed line.
1616071d4279SBram Moolenaar * - lnum is not before the lines with saved states.
1617071d4279SBram Moolenaar * - lnum is not past the lines with saved states.
1618071d4279SBram Moolenaar * - lnum is at or before the last changed line.
1619071d4279SBram Moolenaar */
1620071d4279SBram Moolenaar if (VALID_STATE(¤t_state) && lnum == current_lnum + 1)
1621071d4279SBram Moolenaar {
1622071d4279SBram Moolenaar sp = syn_stack_find_entry(lnum);
1623071d4279SBram Moolenaar if (sp != NULL && sp->sst_lnum == lnum)
1624071d4279SBram Moolenaar {
1625071d4279SBram Moolenaar /*
1626071d4279SBram Moolenaar * finish the previous line (needed when not all of the line was
1627071d4279SBram Moolenaar * drawn)
1628071d4279SBram Moolenaar */
1629071d4279SBram Moolenaar (void)syn_finish_line(FALSE);
1630071d4279SBram Moolenaar
1631071d4279SBram Moolenaar /*
1632071d4279SBram Moolenaar * Compare the current state with the previously saved state of
1633071d4279SBram Moolenaar * the line.
1634071d4279SBram Moolenaar */
1635071d4279SBram Moolenaar if (syn_stack_equal(sp))
1636071d4279SBram Moolenaar retval = FALSE;
1637071d4279SBram Moolenaar
1638071d4279SBram Moolenaar /*
1639071d4279SBram Moolenaar * Store the current state in b_sst_array[] for later use.
1640071d4279SBram Moolenaar */
1641071d4279SBram Moolenaar ++current_lnum;
1642dbe31750SBram Moolenaar (void)store_current_state();
1643071d4279SBram Moolenaar }
1644071d4279SBram Moolenaar }
1645071d4279SBram Moolenaar
1646071d4279SBram Moolenaar return retval;
1647071d4279SBram Moolenaar }
1648071d4279SBram Moolenaar
1649071d4279SBram Moolenaar /*
1650071d4279SBram Moolenaar * Finish the current line.
1651071d4279SBram Moolenaar * This doesn't return any attributes, it only gets the state at the end of
1652071d4279SBram Moolenaar * the line. It can start anywhere in the line, as long as the current state
1653071d4279SBram Moolenaar * is valid.
1654071d4279SBram Moolenaar */
1655071d4279SBram Moolenaar static int
syn_finish_line(int syncing)1656764b23c8SBram Moolenaar syn_finish_line(
16570d6f5d97SBram Moolenaar int syncing) // called for syncing
1658071d4279SBram Moolenaar {
1659071d4279SBram Moolenaar stateitem_T *cur_si;
166081366db6SBram Moolenaar colnr_T prev_current_col;
1661071d4279SBram Moolenaar
1662071d4279SBram Moolenaar while (!current_finished)
1663071d4279SBram Moolenaar {
166456cefaf1SBram Moolenaar (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1665071d4279SBram Moolenaar /*
1666071d4279SBram Moolenaar * When syncing, and found some item, need to check the item.
1667071d4279SBram Moolenaar */
1668071d4279SBram Moolenaar if (syncing && current_state.ga_len)
1669071d4279SBram Moolenaar {
1670071d4279SBram Moolenaar /*
1671071d4279SBram Moolenaar * Check for match with sync item.
1672071d4279SBram Moolenaar */
1673071d4279SBram Moolenaar cur_si = &CUR_STATE(current_state.ga_len - 1);
1674071d4279SBram Moolenaar if (cur_si->si_idx >= 0
1675860cae1cSBram Moolenaar && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1676071d4279SBram Moolenaar & (HL_SYNC_HERE|HL_SYNC_THERE)))
1677071d4279SBram Moolenaar return TRUE;
1678071d4279SBram Moolenaar
16790d6f5d97SBram Moolenaar // syn_current_attr() will have skipped the check for an item
16800d6f5d97SBram Moolenaar // that ends here, need to do that now. Be careful not to go
16810d6f5d97SBram Moolenaar // past the NUL.
168281366db6SBram Moolenaar prev_current_col = current_col;
168381366db6SBram Moolenaar if (syn_getcurline()[current_col] != NUL)
1684071d4279SBram Moolenaar ++current_col;
1685071d4279SBram Moolenaar check_state_ends();
168681366db6SBram Moolenaar current_col = prev_current_col;
1687071d4279SBram Moolenaar }
1688071d4279SBram Moolenaar ++current_col;
1689071d4279SBram Moolenaar }
1690071d4279SBram Moolenaar return FALSE;
1691071d4279SBram Moolenaar }
1692071d4279SBram Moolenaar
1693071d4279SBram Moolenaar /*
1694071d4279SBram Moolenaar * Return highlight attributes for next character.
1695071d4279SBram Moolenaar * Must first call syntax_start() once for the line.
1696071d4279SBram Moolenaar * "col" is normally 0 for the first use in a line, and increments by one each
1697071d4279SBram Moolenaar * time. It's allowed to skip characters and to stop before the end of the
1698071d4279SBram Moolenaar * line. But only a "col" after a previously used column is allowed.
1699217ad920SBram Moolenaar * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1700217ad920SBram Moolenaar * done.
1701071d4279SBram Moolenaar */
1702071d4279SBram Moolenaar int
get_syntax_attr(colnr_T col,int * can_spell,int keep_state)1703764b23c8SBram Moolenaar get_syntax_attr(
1704764b23c8SBram Moolenaar colnr_T col,
1705764b23c8SBram Moolenaar int *can_spell,
17060d6f5d97SBram Moolenaar int keep_state) // keep state of char at "col"
1707071d4279SBram Moolenaar {
1708071d4279SBram Moolenaar int attr = 0;
1709071d4279SBram Moolenaar
1710349955a2SBram Moolenaar if (can_spell != NULL)
17110d6f5d97SBram Moolenaar // Default: Only do spelling when there is no @Spell cluster or when
17120d6f5d97SBram Moolenaar // ":syn spell toplevel" was used.
1713860cae1cSBram Moolenaar *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1714860cae1cSBram Moolenaar ? (syn_block->b_spell_cluster_id == 0)
1715860cae1cSBram Moolenaar : (syn_block->b_syn_spell == SYNSPL_TOP);
1716349955a2SBram Moolenaar
17170d6f5d97SBram Moolenaar // check for out of memory situation
1718860cae1cSBram Moolenaar if (syn_block->b_sst_array == NULL)
1719071d4279SBram Moolenaar return 0;
1720071d4279SBram Moolenaar
17210d6f5d97SBram Moolenaar // After 'synmaxcol' the attribute is always zero.
172284fb85aaSBram Moolenaar if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
1723ce0842a6SBram Moolenaar {
1724ce0842a6SBram Moolenaar clear_current_state();
1725ce0842a6SBram Moolenaar #ifdef FEAT_EVAL
1726ce0842a6SBram Moolenaar current_id = 0;
1727ce0842a6SBram Moolenaar current_trans_id = 0;
1728ce0842a6SBram Moolenaar #endif
17297510fe74SBram Moolenaar #ifdef FEAT_CONCEAL
17307510fe74SBram Moolenaar current_flags = 0;
1731cc0750dcSBram Moolenaar current_seqnr = 0;
17327510fe74SBram Moolenaar #endif
1733ce0842a6SBram Moolenaar return 0;
1734ce0842a6SBram Moolenaar }
1735ce0842a6SBram Moolenaar
17360d6f5d97SBram Moolenaar // Make sure current_state is valid
1737071d4279SBram Moolenaar if (INVALID_STATE(¤t_state))
1738071d4279SBram Moolenaar validate_current_state();
1739071d4279SBram Moolenaar
1740071d4279SBram Moolenaar /*
1741071d4279SBram Moolenaar * Skip from the current column to "col", get the attributes for "col".
1742071d4279SBram Moolenaar */
1743071d4279SBram Moolenaar while (current_col <= col)
1744071d4279SBram Moolenaar {
174556cefaf1SBram Moolenaar attr = syn_current_attr(FALSE, TRUE, can_spell,
174656cefaf1SBram Moolenaar current_col == col ? keep_state : FALSE);
1747071d4279SBram Moolenaar ++current_col;
1748071d4279SBram Moolenaar }
1749071d4279SBram Moolenaar
1750071d4279SBram Moolenaar return attr;
1751071d4279SBram Moolenaar }
1752071d4279SBram Moolenaar
1753071d4279SBram Moolenaar /*
1754071d4279SBram Moolenaar * Get syntax attributes for current_lnum, current_col.
1755071d4279SBram Moolenaar */
1756071d4279SBram Moolenaar static int
syn_current_attr(int syncing,int displaying,int * can_spell,int keep_state)1757764b23c8SBram Moolenaar syn_current_attr(
17580d6f5d97SBram Moolenaar int syncing, // When 1: called for syncing
17590d6f5d97SBram Moolenaar int displaying, // result will be displayed
17600d6f5d97SBram Moolenaar int *can_spell, // return: do spell checking
17610d6f5d97SBram Moolenaar int keep_state) // keep syntax stack afterwards
1762071d4279SBram Moolenaar {
1763071d4279SBram Moolenaar int syn_id;
17640d6f5d97SBram Moolenaar lpos_T endpos; // was: char_u *endp;
17650d6f5d97SBram Moolenaar lpos_T hl_startpos; // was: int hl_startcol;
1766071d4279SBram Moolenaar lpos_T hl_endpos;
17670d6f5d97SBram Moolenaar lpos_T eos_pos; // end-of-start match (start region)
17680d6f5d97SBram Moolenaar lpos_T eoe_pos; // end-of-end pattern
17690d6f5d97SBram Moolenaar int end_idx; // group ID for end pattern
1770071d4279SBram Moolenaar int idx;
1771071d4279SBram Moolenaar synpat_T *spp;
1772217ad920SBram Moolenaar stateitem_T *cur_si, *sip = NULL;
1773071d4279SBram Moolenaar int startcol;
1774071d4279SBram Moolenaar int endcol;
1775071d4279SBram Moolenaar long flags;
1776860cae1cSBram Moolenaar int cchar;
1777071d4279SBram Moolenaar short *next_list;
17780d6f5d97SBram Moolenaar int found_match; // found usable match
17790d6f5d97SBram Moolenaar static int try_next_column = FALSE; // must try in next col
1780071d4279SBram Moolenaar int do_keywords;
1781071d4279SBram Moolenaar regmmatch_T regmatch;
1782071d4279SBram Moolenaar lpos_T pos;
1783071d4279SBram Moolenaar int lc_col;
1784071d4279SBram Moolenaar reg_extmatch_T *cur_extmatch = NULL;
17850d6f5d97SBram Moolenaar char_u buf_chartab[32]; // chartab array for syn iskyeyword
17860d6f5d97SBram Moolenaar char_u *line; // current line. NOTE: becomes invalid after
17870d6f5d97SBram Moolenaar // looking for a pattern match!
1788071d4279SBram Moolenaar
17890d6f5d97SBram Moolenaar // variables for zero-width matches that have a "nextgroup" argument
1790071d4279SBram Moolenaar int keep_next_list;
1791071d4279SBram Moolenaar int zero_width_next_list = FALSE;
1792071d4279SBram Moolenaar garray_T zero_width_next_ga;
1793071d4279SBram Moolenaar
1794071d4279SBram Moolenaar /*
1795071d4279SBram Moolenaar * No character, no attributes! Past end of line?
1796071d4279SBram Moolenaar * Do try matching with an empty line (could be the start of a region).
1797071d4279SBram Moolenaar */
1798071d4279SBram Moolenaar line = syn_getcurline();
1799071d4279SBram Moolenaar if (line[current_col] == NUL && current_col != 0)
1800071d4279SBram Moolenaar {
1801071d4279SBram Moolenaar /*
1802071d4279SBram Moolenaar * If we found a match after the last column, use it.
1803071d4279SBram Moolenaar */
1804071d4279SBram Moolenaar if (next_match_idx >= 0 && next_match_col >= (int)current_col
1805071d4279SBram Moolenaar && next_match_col != MAXCOL)
1806071d4279SBram Moolenaar (void)push_next_match(NULL);
1807071d4279SBram Moolenaar
1808071d4279SBram Moolenaar current_finished = TRUE;
1809071d4279SBram Moolenaar current_state_stored = FALSE;
1810071d4279SBram Moolenaar return 0;
1811071d4279SBram Moolenaar }
1812071d4279SBram Moolenaar
18130d6f5d97SBram Moolenaar // if the current or next character is NUL, we will finish the line now
1814071d4279SBram Moolenaar if (line[current_col] == NUL || line[current_col + 1] == NUL)
1815071d4279SBram Moolenaar {
1816071d4279SBram Moolenaar current_finished = TRUE;
1817071d4279SBram Moolenaar current_state_stored = FALSE;
1818071d4279SBram Moolenaar }
1819071d4279SBram Moolenaar
1820071d4279SBram Moolenaar /*
1821071d4279SBram Moolenaar * When in the previous column there was a match but it could not be used
1822071d4279SBram Moolenaar * (empty match or already matched in this column) need to try again in
1823071d4279SBram Moolenaar * the next column.
1824071d4279SBram Moolenaar */
1825071d4279SBram Moolenaar if (try_next_column)
1826071d4279SBram Moolenaar {
1827071d4279SBram Moolenaar next_match_idx = -1;
1828071d4279SBram Moolenaar try_next_column = FALSE;
1829071d4279SBram Moolenaar }
1830071d4279SBram Moolenaar
18310d6f5d97SBram Moolenaar // Only check for keywords when not syncing and there are some.
1832071d4279SBram Moolenaar do_keywords = !syncing
1833860cae1cSBram Moolenaar && (syn_block->b_keywtab.ht_used > 0
1834860cae1cSBram Moolenaar || syn_block->b_keywtab_ic.ht_used > 0);
1835071d4279SBram Moolenaar
18360d6f5d97SBram Moolenaar // Init the list of zero-width matches with a nextlist. This is used to
18370d6f5d97SBram Moolenaar // avoid matching the same item in the same position twice.
1838071d4279SBram Moolenaar ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1839071d4279SBram Moolenaar
18400d6f5d97SBram Moolenaar // use syntax iskeyword option
1841b8060fe8SBram Moolenaar save_chartab(buf_chartab);
1842b8060fe8SBram Moolenaar
1843071d4279SBram Moolenaar /*
1844071d4279SBram Moolenaar * Repeat matching keywords and patterns, to find contained items at the
1845071d4279SBram Moolenaar * same column. This stops when there are no extra matches at the current
1846071d4279SBram Moolenaar * column.
1847071d4279SBram Moolenaar */
1848071d4279SBram Moolenaar do
1849071d4279SBram Moolenaar {
1850071d4279SBram Moolenaar found_match = FALSE;
1851071d4279SBram Moolenaar keep_next_list = FALSE;
1852071d4279SBram Moolenaar syn_id = 0;
1853071d4279SBram Moolenaar
1854b8060fe8SBram Moolenaar
1855071d4279SBram Moolenaar /*
1856071d4279SBram Moolenaar * 1. Check for a current state.
1857071d4279SBram Moolenaar * Only when there is no current state, or if the current state may
1858071d4279SBram Moolenaar * contain other things, we need to check for keywords and patterns.
1859071d4279SBram Moolenaar * Always need to check for contained items if some item has the
1860071d4279SBram Moolenaar * "containedin" argument (takes extra time!).
1861071d4279SBram Moolenaar */
1862071d4279SBram Moolenaar if (current_state.ga_len)
1863071d4279SBram Moolenaar cur_si = &CUR_STATE(current_state.ga_len - 1);
1864071d4279SBram Moolenaar else
1865071d4279SBram Moolenaar cur_si = NULL;
1866071d4279SBram Moolenaar
1867860cae1cSBram Moolenaar if (syn_block->b_syn_containedin || cur_si == NULL
1868071d4279SBram Moolenaar || cur_si->si_cont_list != NULL)
1869071d4279SBram Moolenaar {
1870071d4279SBram Moolenaar /*
1871071d4279SBram Moolenaar * 2. Check for keywords, if on a keyword char after a non-keyword
1872071d4279SBram Moolenaar * char. Don't do this when syncing.
1873071d4279SBram Moolenaar */
1874071d4279SBram Moolenaar if (do_keywords)
1875071d4279SBram Moolenaar {
1876071d4279SBram Moolenaar line = syn_getcurline();
18779d182dd0SBram Moolenaar if (vim_iswordp_buf(line + current_col, syn_buf)
1878071d4279SBram Moolenaar && (current_col == 0
18799d182dd0SBram Moolenaar || !vim_iswordp_buf(line + current_col - 1
1880071d4279SBram Moolenaar - (has_mbyte
1881071d4279SBram Moolenaar ? (*mb_head_off)(line, line + current_col - 1)
1882264b74faSBram Moolenaar : 0) , syn_buf)))
1883071d4279SBram Moolenaar {
1884071d4279SBram Moolenaar syn_id = check_keyword_id(line, (int)current_col,
1885860cae1cSBram Moolenaar &endcol, &flags, &next_list, cur_si,
1886860cae1cSBram Moolenaar &cchar);
1887e2cc9702SBram Moolenaar if (syn_id != 0)
1888071d4279SBram Moolenaar {
1889071d4279SBram Moolenaar if (push_current_state(KEYWORD_IDX) == OK)
1890071d4279SBram Moolenaar {
1891071d4279SBram Moolenaar cur_si = &CUR_STATE(current_state.ga_len - 1);
1892071d4279SBram Moolenaar cur_si->si_m_startcol = current_col;
1893071d4279SBram Moolenaar cur_si->si_h_startpos.lnum = current_lnum;
18940d6f5d97SBram Moolenaar cur_si->si_h_startpos.col = 0; // starts right away
1895071d4279SBram Moolenaar cur_si->si_m_endpos.lnum = current_lnum;
1896071d4279SBram Moolenaar cur_si->si_m_endpos.col = endcol;
1897071d4279SBram Moolenaar cur_si->si_h_endpos.lnum = current_lnum;
1898071d4279SBram Moolenaar cur_si->si_h_endpos.col = endcol;
1899071d4279SBram Moolenaar cur_si->si_ends = TRUE;
1900071d4279SBram Moolenaar cur_si->si_end_idx = 0;
1901071d4279SBram Moolenaar cur_si->si_flags = flags;
1902860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
1903ffbbcb59SBram Moolenaar cur_si->si_seqnr = next_seqnr++;
19046e202e52SBram Moolenaar cur_si->si_cchar = cchar;
1905860cae1cSBram Moolenaar if (current_state.ga_len > 1)
1906860cae1cSBram Moolenaar cur_si->si_flags |=
1907860cae1cSBram Moolenaar CUR_STATE(current_state.ga_len - 2).si_flags
1908860cae1cSBram Moolenaar & HL_CONCEAL;
1909860cae1cSBram Moolenaar #endif
1910071d4279SBram Moolenaar cur_si->si_id = syn_id;
1911071d4279SBram Moolenaar cur_si->si_trans_id = syn_id;
1912071d4279SBram Moolenaar if (flags & HL_TRANSP)
1913071d4279SBram Moolenaar {
1914071d4279SBram Moolenaar if (current_state.ga_len < 2)
1915071d4279SBram Moolenaar {
1916071d4279SBram Moolenaar cur_si->si_attr = 0;
1917071d4279SBram Moolenaar cur_si->si_trans_id = 0;
1918071d4279SBram Moolenaar }
1919071d4279SBram Moolenaar else
1920071d4279SBram Moolenaar {
1921071d4279SBram Moolenaar cur_si->si_attr = CUR_STATE(
1922071d4279SBram Moolenaar current_state.ga_len - 2).si_attr;
1923071d4279SBram Moolenaar cur_si->si_trans_id = CUR_STATE(
1924071d4279SBram Moolenaar current_state.ga_len - 2).si_trans_id;
1925071d4279SBram Moolenaar }
1926071d4279SBram Moolenaar }
1927071d4279SBram Moolenaar else
1928071d4279SBram Moolenaar cur_si->si_attr = syn_id2attr(syn_id);
1929071d4279SBram Moolenaar cur_si->si_cont_list = NULL;
1930071d4279SBram Moolenaar cur_si->si_next_list = next_list;
1931071d4279SBram Moolenaar check_keepend();
1932071d4279SBram Moolenaar }
1933071d4279SBram Moolenaar else
1934071d4279SBram Moolenaar vim_free(next_list);
1935071d4279SBram Moolenaar }
1936071d4279SBram Moolenaar }
1937071d4279SBram Moolenaar }
1938071d4279SBram Moolenaar
1939071d4279SBram Moolenaar /*
1940e2cc9702SBram Moolenaar * 3. Check for patterns (only if no keyword found).
1941071d4279SBram Moolenaar */
1942860cae1cSBram Moolenaar if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
1943071d4279SBram Moolenaar {
1944071d4279SBram Moolenaar /*
1945071d4279SBram Moolenaar * If we didn't check for a match yet, or we are past it, check
1946071d4279SBram Moolenaar * for any match with a pattern.
1947071d4279SBram Moolenaar */
1948071d4279SBram Moolenaar if (next_match_idx < 0 || next_match_col < (int)current_col)
1949071d4279SBram Moolenaar {
1950071d4279SBram Moolenaar /*
1951071d4279SBram Moolenaar * Check all relevant patterns for a match at this
1952071d4279SBram Moolenaar * position. This is complicated, because matching with a
1953071d4279SBram Moolenaar * pattern takes quite a bit of time, thus we want to
1954071d4279SBram Moolenaar * avoid doing it when it's not needed.
1955071d4279SBram Moolenaar */
19560d6f5d97SBram Moolenaar next_match_idx = 0; // no match in this line yet
1957071d4279SBram Moolenaar next_match_col = MAXCOL;
1958860cae1cSBram Moolenaar for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
1959071d4279SBram Moolenaar {
1960860cae1cSBram Moolenaar spp = &(SYN_ITEMS(syn_block)[idx]);
1961071d4279SBram Moolenaar if ( spp->sp_syncing == syncing
1962071d4279SBram Moolenaar && (displaying || !(spp->sp_flags & HL_DISPLAY))
1963071d4279SBram Moolenaar && (spp->sp_type == SPTYPE_MATCH
1964071d4279SBram Moolenaar || spp->sp_type == SPTYPE_START)
1965071d4279SBram Moolenaar && (current_next_list != NULL
1966071d4279SBram Moolenaar ? in_id_list(NULL, current_next_list,
1967071d4279SBram Moolenaar &spp->sp_syn, 0)
1968071d4279SBram Moolenaar : (cur_si == NULL
1969071d4279SBram Moolenaar ? !(spp->sp_flags & HL_CONTAINED)
1970071d4279SBram Moolenaar : in_id_list(cur_si,
1971071d4279SBram Moolenaar cur_si->si_cont_list, &spp->sp_syn,
1972071d4279SBram Moolenaar spp->sp_flags & HL_CONTAINED))))
1973071d4279SBram Moolenaar {
1974dffa5b8eSBram Moolenaar int r;
1975dffa5b8eSBram Moolenaar
19760d6f5d97SBram Moolenaar // If we already tried matching in this line, and
19770d6f5d97SBram Moolenaar // there isn't a match before next_match_col, skip
19780d6f5d97SBram Moolenaar // this item.
1979071d4279SBram Moolenaar if (spp->sp_line_id == current_line_id
1980071d4279SBram Moolenaar && spp->sp_startcol >= next_match_col)
1981071d4279SBram Moolenaar continue;
1982071d4279SBram Moolenaar spp->sp_line_id = current_line_id;
1983071d4279SBram Moolenaar
1984071d4279SBram Moolenaar lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
1985071d4279SBram Moolenaar if (lc_col < 0)
1986071d4279SBram Moolenaar lc_col = 0;
1987071d4279SBram Moolenaar
1988071d4279SBram Moolenaar regmatch.rmm_ic = spp->sp_ic;
1989071d4279SBram Moolenaar regmatch.regprog = spp->sp_prog;
1990dffa5b8eSBram Moolenaar r = syn_regexec(®match,
19918a7f5a2dSBram Moolenaar current_lnum,
19928a7f5a2dSBram Moolenaar (colnr_T)lc_col,
1993dffa5b8eSBram Moolenaar IF_SYN_TIME(&spp->sp_time));
1994dffa5b8eSBram Moolenaar spp->sp_prog = regmatch.regprog;
1995dffa5b8eSBram Moolenaar if (!r)
1996071d4279SBram Moolenaar {
19970d6f5d97SBram Moolenaar // no match in this line, try another one
1998071d4279SBram Moolenaar spp->sp_startcol = MAXCOL;
1999071d4279SBram Moolenaar continue;
2000071d4279SBram Moolenaar }
2001071d4279SBram Moolenaar
2002071d4279SBram Moolenaar /*
2003071d4279SBram Moolenaar * Compute the first column of the match.
2004071d4279SBram Moolenaar */
2005071d4279SBram Moolenaar syn_add_start_off(&pos, ®match,
2006071d4279SBram Moolenaar spp, SPO_MS_OFF, -1);
2007071d4279SBram Moolenaar if (pos.lnum > current_lnum)
2008071d4279SBram Moolenaar {
20090d6f5d97SBram Moolenaar // must have used end of match in a next line,
20100d6f5d97SBram Moolenaar // we can't handle that
2011071d4279SBram Moolenaar spp->sp_startcol = MAXCOL;
2012071d4279SBram Moolenaar continue;
2013071d4279SBram Moolenaar }
2014071d4279SBram Moolenaar startcol = pos.col;
2015071d4279SBram Moolenaar
20160d6f5d97SBram Moolenaar // remember the next column where this pattern
20170d6f5d97SBram Moolenaar // matches in the current line
2018071d4279SBram Moolenaar spp->sp_startcol = startcol;
2019071d4279SBram Moolenaar
2020071d4279SBram Moolenaar /*
2021071d4279SBram Moolenaar * If a previously found match starts at a lower
2022071d4279SBram Moolenaar * column number, don't use this one.
2023071d4279SBram Moolenaar */
2024071d4279SBram Moolenaar if (startcol >= next_match_col)
2025071d4279SBram Moolenaar continue;
2026071d4279SBram Moolenaar
2027071d4279SBram Moolenaar /*
2028071d4279SBram Moolenaar * If we matched this pattern at this position
2029071d4279SBram Moolenaar * before, skip it. Must retry in the next
2030071d4279SBram Moolenaar * column, because it may match from there.
2031071d4279SBram Moolenaar */
2032071d4279SBram Moolenaar if (did_match_already(idx, &zero_width_next_ga))
2033071d4279SBram Moolenaar {
2034071d4279SBram Moolenaar try_next_column = TRUE;
2035071d4279SBram Moolenaar continue;
2036071d4279SBram Moolenaar }
2037071d4279SBram Moolenaar
2038071d4279SBram Moolenaar endpos.lnum = regmatch.endpos[0].lnum;
2039071d4279SBram Moolenaar endpos.col = regmatch.endpos[0].col;
2040071d4279SBram Moolenaar
20410d6f5d97SBram Moolenaar // Compute the highlight start.
2042071d4279SBram Moolenaar syn_add_start_off(&hl_startpos, ®match,
2043071d4279SBram Moolenaar spp, SPO_HS_OFF, -1);
2044071d4279SBram Moolenaar
20450d6f5d97SBram Moolenaar // Compute the region start.
20460d6f5d97SBram Moolenaar // Default is to use the end of the match.
2047071d4279SBram Moolenaar syn_add_end_off(&eos_pos, ®match,
2048071d4279SBram Moolenaar spp, SPO_RS_OFF, 0);
2049071d4279SBram Moolenaar
2050071d4279SBram Moolenaar /*
2051071d4279SBram Moolenaar * Grab the external submatches before they get
2052071d4279SBram Moolenaar * overwritten. Reference count doesn't change.
2053071d4279SBram Moolenaar */
2054071d4279SBram Moolenaar unref_extmatch(cur_extmatch);
2055071d4279SBram Moolenaar cur_extmatch = re_extmatch_out;
2056071d4279SBram Moolenaar re_extmatch_out = NULL;
2057071d4279SBram Moolenaar
2058071d4279SBram Moolenaar flags = 0;
20590d6f5d97SBram Moolenaar eoe_pos.lnum = 0; // avoid warning
2060071d4279SBram Moolenaar eoe_pos.col = 0;
2061071d4279SBram Moolenaar end_idx = 0;
2062071d4279SBram Moolenaar hl_endpos.lnum = 0;
2063071d4279SBram Moolenaar
2064071d4279SBram Moolenaar /*
2065071d4279SBram Moolenaar * For a "oneline" the end must be found in the
2066071d4279SBram Moolenaar * same line too. Search for it after the end of
2067071d4279SBram Moolenaar * the match with the start pattern. Set the
2068071d4279SBram Moolenaar * resulting end positions at the same time.
2069071d4279SBram Moolenaar */
2070071d4279SBram Moolenaar if (spp->sp_type == SPTYPE_START
2071071d4279SBram Moolenaar && (spp->sp_flags & HL_ONELINE))
2072071d4279SBram Moolenaar {
2073071d4279SBram Moolenaar lpos_T startpos;
2074071d4279SBram Moolenaar
2075071d4279SBram Moolenaar startpos = endpos;
2076071d4279SBram Moolenaar find_endpos(idx, &startpos, &endpos, &hl_endpos,
2077071d4279SBram Moolenaar &flags, &eoe_pos, &end_idx, cur_extmatch);
2078071d4279SBram Moolenaar if (endpos.lnum == 0)
20790d6f5d97SBram Moolenaar continue; // not found
2080071d4279SBram Moolenaar }
2081071d4279SBram Moolenaar
2082071d4279SBram Moolenaar /*
2083071d4279SBram Moolenaar * For a "match" the size must be > 0 after the
2084071d4279SBram Moolenaar * end offset needs has been added. Except when
2085071d4279SBram Moolenaar * syncing.
2086071d4279SBram Moolenaar */
2087071d4279SBram Moolenaar else if (spp->sp_type == SPTYPE_MATCH)
2088071d4279SBram Moolenaar {
2089071d4279SBram Moolenaar syn_add_end_off(&hl_endpos, ®match, spp,
2090071d4279SBram Moolenaar SPO_HE_OFF, 0);
2091071d4279SBram Moolenaar syn_add_end_off(&endpos, ®match, spp,
2092071d4279SBram Moolenaar SPO_ME_OFF, 0);
2093071d4279SBram Moolenaar if (endpos.lnum == current_lnum
2094071d4279SBram Moolenaar && (int)endpos.col + syncing < startcol)
2095071d4279SBram Moolenaar {
2096071d4279SBram Moolenaar /*
2097071d4279SBram Moolenaar * If an empty string is matched, may need
2098071d4279SBram Moolenaar * to try matching again at next column.
2099071d4279SBram Moolenaar */
2100071d4279SBram Moolenaar if (regmatch.startpos[0].col
2101071d4279SBram Moolenaar == regmatch.endpos[0].col)
2102071d4279SBram Moolenaar try_next_column = TRUE;
2103071d4279SBram Moolenaar continue;
2104071d4279SBram Moolenaar }
2105071d4279SBram Moolenaar }
2106071d4279SBram Moolenaar
2107071d4279SBram Moolenaar /*
2108071d4279SBram Moolenaar * keep the best match so far in next_match_*
2109071d4279SBram Moolenaar */
21100d6f5d97SBram Moolenaar // Highlighting must start after startpos and end
21110d6f5d97SBram Moolenaar // before endpos.
2112071d4279SBram Moolenaar if (hl_startpos.lnum == current_lnum
2113071d4279SBram Moolenaar && (int)hl_startpos.col < startcol)
2114071d4279SBram Moolenaar hl_startpos.col = startcol;
2115071d4279SBram Moolenaar limit_pos_zero(&hl_endpos, &endpos);
2116071d4279SBram Moolenaar
2117071d4279SBram Moolenaar next_match_idx = idx;
2118071d4279SBram Moolenaar next_match_col = startcol;
2119071d4279SBram Moolenaar next_match_m_endpos = endpos;
2120071d4279SBram Moolenaar next_match_h_endpos = hl_endpos;
2121071d4279SBram Moolenaar next_match_h_startpos = hl_startpos;
2122071d4279SBram Moolenaar next_match_flags = flags;
2123071d4279SBram Moolenaar next_match_eos_pos = eos_pos;
2124071d4279SBram Moolenaar next_match_eoe_pos = eoe_pos;
2125071d4279SBram Moolenaar next_match_end_idx = end_idx;
2126071d4279SBram Moolenaar unref_extmatch(next_match_extmatch);
2127071d4279SBram Moolenaar next_match_extmatch = cur_extmatch;
2128071d4279SBram Moolenaar cur_extmatch = NULL;
2129071d4279SBram Moolenaar }
2130071d4279SBram Moolenaar }
2131071d4279SBram Moolenaar }
2132071d4279SBram Moolenaar
2133071d4279SBram Moolenaar /*
2134071d4279SBram Moolenaar * If we found a match at the current column, use it.
2135071d4279SBram Moolenaar */
2136071d4279SBram Moolenaar if (next_match_idx >= 0 && next_match_col == (int)current_col)
2137071d4279SBram Moolenaar {
2138071d4279SBram Moolenaar synpat_T *lspp;
2139071d4279SBram Moolenaar
21400d6f5d97SBram Moolenaar // When a zero-width item matched which has a nextgroup,
21410d6f5d97SBram Moolenaar // don't push the item but set nextgroup.
2142860cae1cSBram Moolenaar lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
2143071d4279SBram Moolenaar if (next_match_m_endpos.lnum == current_lnum
2144071d4279SBram Moolenaar && next_match_m_endpos.col == current_col
2145071d4279SBram Moolenaar && lspp->sp_next_list != NULL)
2146071d4279SBram Moolenaar {
2147071d4279SBram Moolenaar current_next_list = lspp->sp_next_list;
2148071d4279SBram Moolenaar current_next_flags = lspp->sp_flags;
2149071d4279SBram Moolenaar keep_next_list = TRUE;
2150071d4279SBram Moolenaar zero_width_next_list = TRUE;
2151071d4279SBram Moolenaar
21520d6f5d97SBram Moolenaar // Add the index to a list, so that we can check
21530d6f5d97SBram Moolenaar // later that we don't match it again (and cause an
21540d6f5d97SBram Moolenaar // endless loop).
2155071d4279SBram Moolenaar if (ga_grow(&zero_width_next_ga, 1) == OK)
2156071d4279SBram Moolenaar {
2157071d4279SBram Moolenaar ((int *)(zero_width_next_ga.ga_data))
2158071d4279SBram Moolenaar [zero_width_next_ga.ga_len++] = next_match_idx;
2159071d4279SBram Moolenaar }
2160071d4279SBram Moolenaar next_match_idx = -1;
2161071d4279SBram Moolenaar }
2162071d4279SBram Moolenaar else
2163071d4279SBram Moolenaar cur_si = push_next_match(cur_si);
2164071d4279SBram Moolenaar found_match = TRUE;
2165071d4279SBram Moolenaar }
2166071d4279SBram Moolenaar }
2167071d4279SBram Moolenaar }
2168071d4279SBram Moolenaar
2169071d4279SBram Moolenaar /*
2170071d4279SBram Moolenaar * Handle searching for nextgroup match.
2171071d4279SBram Moolenaar */
2172071d4279SBram Moolenaar if (current_next_list != NULL && !keep_next_list)
2173071d4279SBram Moolenaar {
2174071d4279SBram Moolenaar /*
2175071d4279SBram Moolenaar * If a nextgroup was not found, continue looking for one if:
2176071d4279SBram Moolenaar * - this is an empty line and the "skipempty" option was given
2177071d4279SBram Moolenaar * - we are on white space and the "skipwhite" option was given
2178071d4279SBram Moolenaar */
2179071d4279SBram Moolenaar if (!found_match)
2180071d4279SBram Moolenaar {
2181071d4279SBram Moolenaar line = syn_getcurline();
2182071d4279SBram Moolenaar if (((current_next_flags & HL_SKIPWHITE)
21831c465444SBram Moolenaar && VIM_ISWHITE(line[current_col]))
2184071d4279SBram Moolenaar || ((current_next_flags & HL_SKIPEMPTY)
2185071d4279SBram Moolenaar && *line == NUL))
2186071d4279SBram Moolenaar break;
2187071d4279SBram Moolenaar }
2188071d4279SBram Moolenaar
2189071d4279SBram Moolenaar /*
2190071d4279SBram Moolenaar * If a nextgroup was found: Use it, and continue looking for
2191071d4279SBram Moolenaar * contained matches.
2192071d4279SBram Moolenaar * If a nextgroup was not found: Continue looking for a normal
2193071d4279SBram Moolenaar * match.
2194071d4279SBram Moolenaar * When did set current_next_list for a zero-width item and no
2195071d4279SBram Moolenaar * match was found don't loop (would get stuck).
2196071d4279SBram Moolenaar */
2197071d4279SBram Moolenaar current_next_list = NULL;
2198071d4279SBram Moolenaar next_match_idx = -1;
2199071d4279SBram Moolenaar if (!zero_width_next_list)
2200071d4279SBram Moolenaar found_match = TRUE;
2201071d4279SBram Moolenaar }
2202071d4279SBram Moolenaar
2203071d4279SBram Moolenaar } while (found_match);
2204071d4279SBram Moolenaar
2205b8060fe8SBram Moolenaar restore_chartab(buf_chartab);
2206b8060fe8SBram Moolenaar
2207071d4279SBram Moolenaar /*
2208071d4279SBram Moolenaar * Use attributes from the current state, if within its highlighting.
2209071d4279SBram Moolenaar * If not, use attributes from the current-but-one state, etc.
2210071d4279SBram Moolenaar */
2211071d4279SBram Moolenaar current_attr = 0;
2212071d4279SBram Moolenaar #ifdef FEAT_EVAL
2213071d4279SBram Moolenaar current_id = 0;
2214071d4279SBram Moolenaar current_trans_id = 0;
2215071d4279SBram Moolenaar #endif
2216860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
2217860cae1cSBram Moolenaar current_flags = 0;
2218cc0750dcSBram Moolenaar current_seqnr = 0;
2219860cae1cSBram Moolenaar #endif
2220071d4279SBram Moolenaar if (cur_si != NULL)
2221071d4279SBram Moolenaar {
2222217ad920SBram Moolenaar #ifndef FEAT_EVAL
2223217ad920SBram Moolenaar int current_trans_id = 0;
2224217ad920SBram Moolenaar #endif
2225071d4279SBram Moolenaar for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2226071d4279SBram Moolenaar {
2227071d4279SBram Moolenaar sip = &CUR_STATE(idx);
2228071d4279SBram Moolenaar if ((current_lnum > sip->si_h_startpos.lnum
2229071d4279SBram Moolenaar || (current_lnum == sip->si_h_startpos.lnum
2230071d4279SBram Moolenaar && current_col >= sip->si_h_startpos.col))
2231071d4279SBram Moolenaar && (sip->si_h_endpos.lnum == 0
2232071d4279SBram Moolenaar || current_lnum < sip->si_h_endpos.lnum
2233071d4279SBram Moolenaar || (current_lnum == sip->si_h_endpos.lnum
2234071d4279SBram Moolenaar && current_col < sip->si_h_endpos.col)))
2235071d4279SBram Moolenaar {
2236071d4279SBram Moolenaar current_attr = sip->si_attr;
2237071d4279SBram Moolenaar #ifdef FEAT_EVAL
2238071d4279SBram Moolenaar current_id = sip->si_id;
2239071d4279SBram Moolenaar #endif
2240217ad920SBram Moolenaar current_trans_id = sip->si_trans_id;
2241860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
2242860cae1cSBram Moolenaar current_flags = sip->si_flags;
2243ffbbcb59SBram Moolenaar current_seqnr = sip->si_seqnr;
22446e202e52SBram Moolenaar current_sub_char = sip->si_cchar;
2245860cae1cSBram Moolenaar #endif
2246071d4279SBram Moolenaar break;
2247071d4279SBram Moolenaar }
2248071d4279SBram Moolenaar }
2249071d4279SBram Moolenaar
2250217ad920SBram Moolenaar if (can_spell != NULL)
2251217ad920SBram Moolenaar {
2252217ad920SBram Moolenaar struct sp_syn sps;
2253217ad920SBram Moolenaar
2254217ad920SBram Moolenaar /*
2255217ad920SBram Moolenaar * set "can_spell" to TRUE if spell checking is supposed to be
2256217ad920SBram Moolenaar * done in the current item.
2257217ad920SBram Moolenaar */
2258860cae1cSBram Moolenaar if (syn_block->b_spell_cluster_id == 0)
22596bb68366SBram Moolenaar {
22600d6f5d97SBram Moolenaar // There is no @Spell cluster: Do spelling for items without
22610d6f5d97SBram Moolenaar // @NoSpell cluster.
2262860cae1cSBram Moolenaar if (syn_block->b_nospell_cluster_id == 0
2263860cae1cSBram Moolenaar || current_trans_id == 0)
2264860cae1cSBram Moolenaar *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
22656bb68366SBram Moolenaar else
22666bb68366SBram Moolenaar {
22676bb68366SBram Moolenaar sps.inc_tag = 0;
2268860cae1cSBram Moolenaar sps.id = syn_block->b_nospell_cluster_id;
22696bb68366SBram Moolenaar sps.cont_in_list = NULL;
22706bb68366SBram Moolenaar *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
22716bb68366SBram Moolenaar }
22726bb68366SBram Moolenaar }
22733638c687SBram Moolenaar else
22743638c687SBram Moolenaar {
22750d6f5d97SBram Moolenaar // The @Spell cluster is defined: Do spelling in items with
22760d6f5d97SBram Moolenaar // the @Spell cluster. But not when @NoSpell is also there.
22770d6f5d97SBram Moolenaar // At the toplevel only spell check when ":syn spell toplevel"
22780d6f5d97SBram Moolenaar // was used.
22793638c687SBram Moolenaar if (current_trans_id == 0)
2280860cae1cSBram Moolenaar *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
2281217ad920SBram Moolenaar else
2282217ad920SBram Moolenaar {
2283217ad920SBram Moolenaar sps.inc_tag = 0;
2284860cae1cSBram Moolenaar sps.id = syn_block->b_spell_cluster_id;
2285217ad920SBram Moolenaar sps.cont_in_list = NULL;
2286217ad920SBram Moolenaar *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
22873638c687SBram Moolenaar
2288860cae1cSBram Moolenaar if (syn_block->b_nospell_cluster_id != 0)
22893638c687SBram Moolenaar {
2290860cae1cSBram Moolenaar sps.id = syn_block->b_nospell_cluster_id;
22913638c687SBram Moolenaar if (in_id_list(sip, sip->si_cont_list, &sps, 0))
22923638c687SBram Moolenaar *can_spell = FALSE;
22933638c687SBram Moolenaar }
22943638c687SBram Moolenaar }
2295217ad920SBram Moolenaar }
2296217ad920SBram Moolenaar }
2297217ad920SBram Moolenaar
2298217ad920SBram Moolenaar
2299071d4279SBram Moolenaar /*
2300071d4279SBram Moolenaar * Check for end of current state (and the states before it) at the
2301071d4279SBram Moolenaar * next column. Don't do this for syncing, because we would miss a
2302071d4279SBram Moolenaar * single character match.
2303071d4279SBram Moolenaar * First check if the current state ends at the current column. It
2304071d4279SBram Moolenaar * may be for an empty match and a containing item might end in the
2305071d4279SBram Moolenaar * current column.
2306071d4279SBram Moolenaar */
230756cefaf1SBram Moolenaar if (!syncing && !keep_state)
2308071d4279SBram Moolenaar {
2309071d4279SBram Moolenaar check_state_ends();
231081366db6SBram Moolenaar if (current_state.ga_len > 0
231181366db6SBram Moolenaar && syn_getcurline()[current_col] != NUL)
2312071d4279SBram Moolenaar {
2313071d4279SBram Moolenaar ++current_col;
2314071d4279SBram Moolenaar check_state_ends();
2315071d4279SBram Moolenaar --current_col;
2316071d4279SBram Moolenaar }
2317071d4279SBram Moolenaar }
2318071d4279SBram Moolenaar }
2319217ad920SBram Moolenaar else if (can_spell != NULL)
23200d6f5d97SBram Moolenaar // Default: Only do spelling when there is no @Spell cluster or when
23210d6f5d97SBram Moolenaar // ":syn spell toplevel" was used.
2322860cae1cSBram Moolenaar *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2323860cae1cSBram Moolenaar ? (syn_block->b_spell_cluster_id == 0)
2324860cae1cSBram Moolenaar : (syn_block->b_syn_spell == SYNSPL_TOP);
2325071d4279SBram Moolenaar
23260d6f5d97SBram Moolenaar // nextgroup ends at end of line, unless "skipnl" or "skipempty" present
2327071d4279SBram Moolenaar if (current_next_list != NULL
2328069dafc1SBram Moolenaar && (line = syn_getcurline())[current_col] != NUL
2329069dafc1SBram Moolenaar && line[current_col + 1] == NUL
2330071d4279SBram Moolenaar && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2331071d4279SBram Moolenaar current_next_list = NULL;
2332071d4279SBram Moolenaar
2333071d4279SBram Moolenaar if (zero_width_next_ga.ga_len > 0)
2334071d4279SBram Moolenaar ga_clear(&zero_width_next_ga);
2335071d4279SBram Moolenaar
23360d6f5d97SBram Moolenaar // No longer need external matches. But keep next_match_extmatch.
2337071d4279SBram Moolenaar unref_extmatch(re_extmatch_out);
2338071d4279SBram Moolenaar re_extmatch_out = NULL;
2339071d4279SBram Moolenaar unref_extmatch(cur_extmatch);
2340071d4279SBram Moolenaar
2341071d4279SBram Moolenaar return current_attr;
2342071d4279SBram Moolenaar }
2343071d4279SBram Moolenaar
2344071d4279SBram Moolenaar
2345071d4279SBram Moolenaar /*
2346071d4279SBram Moolenaar * Check if we already matched pattern "idx" at the current column.
2347071d4279SBram Moolenaar */
2348071d4279SBram Moolenaar static int
did_match_already(int idx,garray_T * gap)2349764b23c8SBram Moolenaar did_match_already(int idx, garray_T *gap)
2350071d4279SBram Moolenaar {
2351071d4279SBram Moolenaar int i;
2352071d4279SBram Moolenaar
2353071d4279SBram Moolenaar for (i = current_state.ga_len; --i >= 0; )
2354071d4279SBram Moolenaar if (CUR_STATE(i).si_m_startcol == (int)current_col
2355071d4279SBram Moolenaar && CUR_STATE(i).si_m_lnum == (int)current_lnum
2356071d4279SBram Moolenaar && CUR_STATE(i).si_idx == idx)
2357071d4279SBram Moolenaar return TRUE;
2358071d4279SBram Moolenaar
23590d6f5d97SBram Moolenaar // Zero-width matches with a nextgroup argument are not put on the syntax
23600d6f5d97SBram Moolenaar // stack, and can only be matched once anyway.
2361071d4279SBram Moolenaar for (i = gap->ga_len; --i >= 0; )
2362071d4279SBram Moolenaar if (((int *)(gap->ga_data))[i] == idx)
2363071d4279SBram Moolenaar return TRUE;
2364071d4279SBram Moolenaar
2365071d4279SBram Moolenaar return FALSE;
2366071d4279SBram Moolenaar }
2367071d4279SBram Moolenaar
2368071d4279SBram Moolenaar /*
2369071d4279SBram Moolenaar * Push the next match onto the stack.
2370071d4279SBram Moolenaar */
2371071d4279SBram Moolenaar static stateitem_T *
push_next_match(stateitem_T * cur_si)2372764b23c8SBram Moolenaar push_next_match(stateitem_T *cur_si)
2373071d4279SBram Moolenaar {
2374071d4279SBram Moolenaar synpat_T *spp;
2375860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
2376860cae1cSBram Moolenaar int save_flags;
2377860cae1cSBram Moolenaar #endif
2378071d4279SBram Moolenaar
2379860cae1cSBram Moolenaar spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
2380071d4279SBram Moolenaar
2381071d4279SBram Moolenaar /*
2382071d4279SBram Moolenaar * Push the item in current_state stack;
2383071d4279SBram Moolenaar */
2384071d4279SBram Moolenaar if (push_current_state(next_match_idx) == OK)
2385071d4279SBram Moolenaar {
2386071d4279SBram Moolenaar /*
2387071d4279SBram Moolenaar * If it's a start-skip-end type that crosses lines, figure out how
2388071d4279SBram Moolenaar * much it continues in this line. Otherwise just fill in the length.
2389071d4279SBram Moolenaar */
2390071d4279SBram Moolenaar cur_si = &CUR_STATE(current_state.ga_len - 1);
2391071d4279SBram Moolenaar cur_si->si_h_startpos = next_match_h_startpos;
2392071d4279SBram Moolenaar cur_si->si_m_startcol = current_col;
2393071d4279SBram Moolenaar cur_si->si_m_lnum = current_lnum;
2394071d4279SBram Moolenaar cur_si->si_flags = spp->sp_flags;
2395860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
2396ffbbcb59SBram Moolenaar cur_si->si_seqnr = next_seqnr++;
23976e202e52SBram Moolenaar cur_si->si_cchar = spp->sp_cchar;
2398860cae1cSBram Moolenaar if (current_state.ga_len > 1)
2399860cae1cSBram Moolenaar cur_si->si_flags |=
2400860cae1cSBram Moolenaar CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2401860cae1cSBram Moolenaar #endif
2402071d4279SBram Moolenaar cur_si->si_next_list = spp->sp_next_list;
2403071d4279SBram Moolenaar cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2404071d4279SBram Moolenaar if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2405071d4279SBram Moolenaar {
24060d6f5d97SBram Moolenaar // Try to find the end pattern in the current line
2407071d4279SBram Moolenaar update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2408071d4279SBram Moolenaar check_keepend();
2409071d4279SBram Moolenaar }
2410071d4279SBram Moolenaar else
2411071d4279SBram Moolenaar {
2412071d4279SBram Moolenaar cur_si->si_m_endpos = next_match_m_endpos;
2413071d4279SBram Moolenaar cur_si->si_h_endpos = next_match_h_endpos;
2414071d4279SBram Moolenaar cur_si->si_ends = TRUE;
2415071d4279SBram Moolenaar cur_si->si_flags |= next_match_flags;
2416071d4279SBram Moolenaar cur_si->si_eoe_pos = next_match_eoe_pos;
2417071d4279SBram Moolenaar cur_si->si_end_idx = next_match_end_idx;
2418071d4279SBram Moolenaar }
2419071d4279SBram Moolenaar if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2420071d4279SBram Moolenaar keepend_level = current_state.ga_len - 1;
2421071d4279SBram Moolenaar check_keepend();
2422071d4279SBram Moolenaar update_si_attr(current_state.ga_len - 1);
2423071d4279SBram Moolenaar
2424860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
2425860cae1cSBram Moolenaar save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2426860cae1cSBram Moolenaar #endif
2427071d4279SBram Moolenaar /*
2428071d4279SBram Moolenaar * If the start pattern has another highlight group, push another item
2429071d4279SBram Moolenaar * on the stack for the start pattern.
2430071d4279SBram Moolenaar */
2431071d4279SBram Moolenaar if ( spp->sp_type == SPTYPE_START
2432071d4279SBram Moolenaar && spp->sp_syn_match_id != 0
2433071d4279SBram Moolenaar && push_current_state(next_match_idx) == OK)
2434071d4279SBram Moolenaar {
2435071d4279SBram Moolenaar cur_si = &CUR_STATE(current_state.ga_len - 1);
2436071d4279SBram Moolenaar cur_si->si_h_startpos = next_match_h_startpos;
2437071d4279SBram Moolenaar cur_si->si_m_startcol = current_col;
2438071d4279SBram Moolenaar cur_si->si_m_lnum = current_lnum;
2439071d4279SBram Moolenaar cur_si->si_m_endpos = next_match_eos_pos;
2440071d4279SBram Moolenaar cur_si->si_h_endpos = next_match_eos_pos;
2441071d4279SBram Moolenaar cur_si->si_ends = TRUE;
2442071d4279SBram Moolenaar cur_si->si_end_idx = 0;
2443071d4279SBram Moolenaar cur_si->si_flags = HL_MATCH;
2444860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
24453b95389dSBram Moolenaar cur_si->si_seqnr = next_seqnr++;
2446860cae1cSBram Moolenaar cur_si->si_flags |= save_flags;
2447860cae1cSBram Moolenaar if (cur_si->si_flags & HL_CONCEALENDS)
2448860cae1cSBram Moolenaar cur_si->si_flags |= HL_CONCEAL;
2449860cae1cSBram Moolenaar #endif
2450071d4279SBram Moolenaar cur_si->si_next_list = NULL;
2451071d4279SBram Moolenaar check_keepend();
2452071d4279SBram Moolenaar update_si_attr(current_state.ga_len - 1);
2453071d4279SBram Moolenaar }
2454071d4279SBram Moolenaar }
2455071d4279SBram Moolenaar
24560d6f5d97SBram Moolenaar next_match_idx = -1; // try other match next time
2457071d4279SBram Moolenaar
2458071d4279SBram Moolenaar return cur_si;
2459071d4279SBram Moolenaar }
2460071d4279SBram Moolenaar
2461071d4279SBram Moolenaar /*
2462071d4279SBram Moolenaar * Check for end of current state (and the states before it).
2463071d4279SBram Moolenaar */
2464071d4279SBram Moolenaar static void
check_state_ends(void)2465764b23c8SBram Moolenaar check_state_ends(void)
2466071d4279SBram Moolenaar {
2467071d4279SBram Moolenaar stateitem_T *cur_si;
24686fa46363SBram Moolenaar int had_extend;
2469071d4279SBram Moolenaar
2470071d4279SBram Moolenaar cur_si = &CUR_STATE(current_state.ga_len - 1);
2471071d4279SBram Moolenaar for (;;)
2472071d4279SBram Moolenaar {
2473071d4279SBram Moolenaar if (cur_si->si_ends
2474071d4279SBram Moolenaar && (cur_si->si_m_endpos.lnum < current_lnum
2475071d4279SBram Moolenaar || (cur_si->si_m_endpos.lnum == current_lnum
2476071d4279SBram Moolenaar && cur_si->si_m_endpos.col <= current_col)))
2477071d4279SBram Moolenaar {
2478071d4279SBram Moolenaar /*
2479071d4279SBram Moolenaar * If there is an end pattern group ID, highlight the end pattern
2480071d4279SBram Moolenaar * now. No need to pop the current item from the stack.
2481071d4279SBram Moolenaar * Only do this if the end pattern continues beyond the current
2482071d4279SBram Moolenaar * position.
2483071d4279SBram Moolenaar */
2484071d4279SBram Moolenaar if (cur_si->si_end_idx
2485071d4279SBram Moolenaar && (cur_si->si_eoe_pos.lnum > current_lnum
2486071d4279SBram Moolenaar || (cur_si->si_eoe_pos.lnum == current_lnum
2487071d4279SBram Moolenaar && cur_si->si_eoe_pos.col > current_col)))
2488071d4279SBram Moolenaar {
2489071d4279SBram Moolenaar cur_si->si_idx = cur_si->si_end_idx;
2490071d4279SBram Moolenaar cur_si->si_end_idx = 0;
2491071d4279SBram Moolenaar cur_si->si_m_endpos = cur_si->si_eoe_pos;
2492071d4279SBram Moolenaar cur_si->si_h_endpos = cur_si->si_eoe_pos;
2493071d4279SBram Moolenaar cur_si->si_flags |= HL_MATCH;
2494860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
24953b95389dSBram Moolenaar cur_si->si_seqnr = next_seqnr++;
2496860cae1cSBram Moolenaar if (cur_si->si_flags & HL_CONCEALENDS)
2497860cae1cSBram Moolenaar cur_si->si_flags |= HL_CONCEAL;
2498860cae1cSBram Moolenaar #endif
2499071d4279SBram Moolenaar update_si_attr(current_state.ga_len - 1);
2500293ee4d4SBram Moolenaar
25010d6f5d97SBram Moolenaar // nextgroup= should not match in the end pattern
25023a7d8c3aSBram Moolenaar current_next_list = NULL;
25033a7d8c3aSBram Moolenaar
25040d6f5d97SBram Moolenaar // what matches next may be different now, clear it
2505293ee4d4SBram Moolenaar next_match_idx = 0;
2506293ee4d4SBram Moolenaar next_match_col = MAXCOL;
2507071d4279SBram Moolenaar break;
2508071d4279SBram Moolenaar }
2509071d4279SBram Moolenaar else
2510071d4279SBram Moolenaar {
25110d6f5d97SBram Moolenaar // handle next_list, unless at end of line and no "skipnl" or
25120d6f5d97SBram Moolenaar // "skipempty"
2513071d4279SBram Moolenaar current_next_list = cur_si->si_next_list;
2514071d4279SBram Moolenaar current_next_flags = cur_si->si_flags;
2515071d4279SBram Moolenaar if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2516071d4279SBram Moolenaar && syn_getcurline()[current_col] == NUL)
2517071d4279SBram Moolenaar current_next_list = NULL;
2518071d4279SBram Moolenaar
25190d6f5d97SBram Moolenaar // When the ended item has "extend", another item with
25200d6f5d97SBram Moolenaar // "keepend" now needs to check for its end.
25216fa46363SBram Moolenaar had_extend = (cur_si->si_flags & HL_EXTEND);
2522071d4279SBram Moolenaar
2523071d4279SBram Moolenaar pop_current_state();
2524071d4279SBram Moolenaar
2525071d4279SBram Moolenaar if (current_state.ga_len == 0)
2526071d4279SBram Moolenaar break;
2527071d4279SBram Moolenaar
252881993f47SBram Moolenaar if (had_extend && keepend_level >= 0)
2529071d4279SBram Moolenaar {
2530071d4279SBram Moolenaar syn_update_ends(FALSE);
2531071d4279SBram Moolenaar if (current_state.ga_len == 0)
2532071d4279SBram Moolenaar break;
2533071d4279SBram Moolenaar }
2534071d4279SBram Moolenaar
2535071d4279SBram Moolenaar cur_si = &CUR_STATE(current_state.ga_len - 1);
2536071d4279SBram Moolenaar
2537071d4279SBram Moolenaar /*
2538071d4279SBram Moolenaar * Only for a region the search for the end continues after
2539071d4279SBram Moolenaar * the end of the contained item. If the contained match
2540071d4279SBram Moolenaar * included the end-of-line, break here, the region continues.
2541071d4279SBram Moolenaar * Don't do this when:
2542071d4279SBram Moolenaar * - "keepend" is used for the contained item
2543071d4279SBram Moolenaar * - not at the end of the line (could be end="x$"me=e-1).
2544071d4279SBram Moolenaar * - "excludenl" is used (HL_HAS_EOL won't be set)
2545071d4279SBram Moolenaar */
2546071d4279SBram Moolenaar if (cur_si->si_idx >= 0
2547860cae1cSBram Moolenaar && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
2548071d4279SBram Moolenaar == SPTYPE_START
2549071d4279SBram Moolenaar && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2550071d4279SBram Moolenaar {
2551071d4279SBram Moolenaar update_si_end(cur_si, (int)current_col, TRUE);
2552071d4279SBram Moolenaar check_keepend();
2553071d4279SBram Moolenaar if ((current_next_flags & HL_HAS_EOL)
2554071d4279SBram Moolenaar && keepend_level < 0
2555071d4279SBram Moolenaar && syn_getcurline()[current_col] == NUL)
2556071d4279SBram Moolenaar break;
2557071d4279SBram Moolenaar }
2558071d4279SBram Moolenaar }
2559071d4279SBram Moolenaar }
2560071d4279SBram Moolenaar else
2561071d4279SBram Moolenaar break;
2562071d4279SBram Moolenaar }
2563071d4279SBram Moolenaar }
2564071d4279SBram Moolenaar
2565071d4279SBram Moolenaar /*
2566071d4279SBram Moolenaar * Update an entry in the current_state stack for a match or region. This
2567071d4279SBram Moolenaar * fills in si_attr, si_next_list and si_cont_list.
2568071d4279SBram Moolenaar */
2569071d4279SBram Moolenaar static void
update_si_attr(int idx)2570764b23c8SBram Moolenaar update_si_attr(int idx)
2571071d4279SBram Moolenaar {
2572071d4279SBram Moolenaar stateitem_T *sip = &CUR_STATE(idx);
2573071d4279SBram Moolenaar synpat_T *spp;
2574071d4279SBram Moolenaar
25750d6f5d97SBram Moolenaar // This should not happen...
25763a36cf7bSBram Moolenaar if (sip->si_idx < 0)
25773a36cf7bSBram Moolenaar return;
25783a36cf7bSBram Moolenaar
2579860cae1cSBram Moolenaar spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
2580071d4279SBram Moolenaar if (sip->si_flags & HL_MATCH)
2581071d4279SBram Moolenaar sip->si_id = spp->sp_syn_match_id;
2582071d4279SBram Moolenaar else
2583071d4279SBram Moolenaar sip->si_id = spp->sp_syn.id;
2584071d4279SBram Moolenaar sip->si_attr = syn_id2attr(sip->si_id);
2585071d4279SBram Moolenaar sip->si_trans_id = sip->si_id;
2586071d4279SBram Moolenaar if (sip->si_flags & HL_MATCH)
2587071d4279SBram Moolenaar sip->si_cont_list = NULL;
2588071d4279SBram Moolenaar else
2589071d4279SBram Moolenaar sip->si_cont_list = spp->sp_cont_list;
2590071d4279SBram Moolenaar
2591071d4279SBram Moolenaar /*
2592071d4279SBram Moolenaar * For transparent items, take attr from outer item.
2593071d4279SBram Moolenaar * Also take cont_list, if there is none.
2594071d4279SBram Moolenaar * Don't do this for the matchgroup of a start or end pattern.
2595071d4279SBram Moolenaar */
2596071d4279SBram Moolenaar if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2597071d4279SBram Moolenaar {
2598071d4279SBram Moolenaar if (idx == 0)
2599071d4279SBram Moolenaar {
2600071d4279SBram Moolenaar sip->si_attr = 0;
2601071d4279SBram Moolenaar sip->si_trans_id = 0;
2602071d4279SBram Moolenaar if (sip->si_cont_list == NULL)
2603071d4279SBram Moolenaar sip->si_cont_list = ID_LIST_ALL;
2604071d4279SBram Moolenaar }
2605071d4279SBram Moolenaar else
2606071d4279SBram Moolenaar {
2607071d4279SBram Moolenaar sip->si_attr = CUR_STATE(idx - 1).si_attr;
2608071d4279SBram Moolenaar sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
2609071d4279SBram Moolenaar if (sip->si_cont_list == NULL)
2610071d4279SBram Moolenaar {
2611071d4279SBram Moolenaar sip->si_flags |= HL_TRANS_CONT;
2612071d4279SBram Moolenaar sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2613071d4279SBram Moolenaar }
2614071d4279SBram Moolenaar }
2615071d4279SBram Moolenaar }
2616071d4279SBram Moolenaar }
2617071d4279SBram Moolenaar
2618071d4279SBram Moolenaar /*
2619071d4279SBram Moolenaar * Check the current stack for patterns with "keepend" flag.
2620071d4279SBram Moolenaar * Propagate the match-end to contained items, until a "skipend" item is found.
2621071d4279SBram Moolenaar */
2622071d4279SBram Moolenaar static void
check_keepend(void)2623764b23c8SBram Moolenaar check_keepend(void)
2624071d4279SBram Moolenaar {
2625071d4279SBram Moolenaar int i;
2626071d4279SBram Moolenaar lpos_T maxpos;
26277bd2cd8dSBram Moolenaar lpos_T maxpos_h;
2628071d4279SBram Moolenaar stateitem_T *sip;
2629071d4279SBram Moolenaar
2630071d4279SBram Moolenaar /*
2631071d4279SBram Moolenaar * This check can consume a lot of time; only do it from the level where
2632071d4279SBram Moolenaar * there really is a keepend.
2633071d4279SBram Moolenaar */
2634071d4279SBram Moolenaar if (keepend_level < 0)
2635071d4279SBram Moolenaar return;
2636071d4279SBram Moolenaar
2637071d4279SBram Moolenaar /*
2638071d4279SBram Moolenaar * Find the last index of an "extend" item. "keepend" items before that
2639071d4279SBram Moolenaar * won't do anything. If there is no "extend" item "i" will be
2640071d4279SBram Moolenaar * "keepend_level" and all "keepend" items will work normally.
2641071d4279SBram Moolenaar */
2642071d4279SBram Moolenaar for (i = current_state.ga_len - 1; i > keepend_level; --i)
2643071d4279SBram Moolenaar if (CUR_STATE(i).si_flags & HL_EXTEND)
2644071d4279SBram Moolenaar break;
2645071d4279SBram Moolenaar
2646071d4279SBram Moolenaar maxpos.lnum = 0;
2647ed39e1d5SBram Moolenaar maxpos.col = 0;
26487bd2cd8dSBram Moolenaar maxpos_h.lnum = 0;
2649ed39e1d5SBram Moolenaar maxpos_h.col = 0;
2650071d4279SBram Moolenaar for ( ; i < current_state.ga_len; ++i)
2651071d4279SBram Moolenaar {
2652071d4279SBram Moolenaar sip = &CUR_STATE(i);
2653071d4279SBram Moolenaar if (maxpos.lnum != 0)
2654071d4279SBram Moolenaar {
2655071d4279SBram Moolenaar limit_pos_zero(&sip->si_m_endpos, &maxpos);
26567bd2cd8dSBram Moolenaar limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
2657071d4279SBram Moolenaar limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2658071d4279SBram Moolenaar sip->si_ends = TRUE;
2659071d4279SBram Moolenaar }
26607bd2cd8dSBram Moolenaar if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
26617bd2cd8dSBram Moolenaar {
26627bd2cd8dSBram Moolenaar if (maxpos.lnum == 0
2663071d4279SBram Moolenaar || maxpos.lnum > sip->si_m_endpos.lnum
2664071d4279SBram Moolenaar || (maxpos.lnum == sip->si_m_endpos.lnum
26657bd2cd8dSBram Moolenaar && maxpos.col > sip->si_m_endpos.col))
2666071d4279SBram Moolenaar maxpos = sip->si_m_endpos;
26677bd2cd8dSBram Moolenaar if (maxpos_h.lnum == 0
26687bd2cd8dSBram Moolenaar || maxpos_h.lnum > sip->si_h_endpos.lnum
26697bd2cd8dSBram Moolenaar || (maxpos_h.lnum == sip->si_h_endpos.lnum
26707bd2cd8dSBram Moolenaar && maxpos_h.col > sip->si_h_endpos.col))
26717bd2cd8dSBram Moolenaar maxpos_h = sip->si_h_endpos;
26727bd2cd8dSBram Moolenaar }
2673071d4279SBram Moolenaar }
2674071d4279SBram Moolenaar }
2675071d4279SBram Moolenaar
2676071d4279SBram Moolenaar /*
2677071d4279SBram Moolenaar * Update an entry in the current_state stack for a start-skip-end pattern.
2678071d4279SBram Moolenaar * This finds the end of the current item, if it's in the current line.
2679071d4279SBram Moolenaar *
2680071d4279SBram Moolenaar * Return the flags for the matched END.
2681071d4279SBram Moolenaar */
2682071d4279SBram Moolenaar static void
update_si_end(stateitem_T * sip,int startcol,int force)2683764b23c8SBram Moolenaar update_si_end(
2684764b23c8SBram Moolenaar stateitem_T *sip,
26850d6f5d97SBram Moolenaar int startcol, // where to start searching for the end
26860d6f5d97SBram Moolenaar int force) // when TRUE overrule a previous end
2687071d4279SBram Moolenaar {
2688071d4279SBram Moolenaar lpos_T startpos;
2689071d4279SBram Moolenaar lpos_T endpos;
2690071d4279SBram Moolenaar lpos_T hl_endpos;
2691071d4279SBram Moolenaar lpos_T end_endpos;
2692071d4279SBram Moolenaar int end_idx;
2693071d4279SBram Moolenaar
26940d6f5d97SBram Moolenaar // return quickly for a keyword
26953a36cf7bSBram Moolenaar if (sip->si_idx < 0)
26963a36cf7bSBram Moolenaar return;
26973a36cf7bSBram Moolenaar
26980d6f5d97SBram Moolenaar // Don't update when it's already done. Can be a match of an end pattern
26990d6f5d97SBram Moolenaar // that started in a previous line. Watch out: can also be a "keepend"
27000d6f5d97SBram Moolenaar // from a containing item.
2701071d4279SBram Moolenaar if (!force && sip->si_m_endpos.lnum >= current_lnum)
2702071d4279SBram Moolenaar return;
2703071d4279SBram Moolenaar
2704071d4279SBram Moolenaar /*
2705071d4279SBram Moolenaar * We need to find the end of the region. It may continue in the next
2706071d4279SBram Moolenaar * line.
2707071d4279SBram Moolenaar */
2708071d4279SBram Moolenaar end_idx = 0;
2709071d4279SBram Moolenaar startpos.lnum = current_lnum;
2710071d4279SBram Moolenaar startpos.col = startcol;
2711071d4279SBram Moolenaar find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2712071d4279SBram Moolenaar &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2713071d4279SBram Moolenaar
2714071d4279SBram Moolenaar if (endpos.lnum == 0)
2715071d4279SBram Moolenaar {
27160d6f5d97SBram Moolenaar // No end pattern matched.
2717860cae1cSBram Moolenaar if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
2718071d4279SBram Moolenaar {
27190d6f5d97SBram Moolenaar // a "oneline" never continues in the next line
2720071d4279SBram Moolenaar sip->si_ends = TRUE;
2721071d4279SBram Moolenaar sip->si_m_endpos.lnum = current_lnum;
2722071d4279SBram Moolenaar sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2723071d4279SBram Moolenaar }
2724071d4279SBram Moolenaar else
2725071d4279SBram Moolenaar {
27260d6f5d97SBram Moolenaar // continues in the next line
2727071d4279SBram Moolenaar sip->si_ends = FALSE;
2728071d4279SBram Moolenaar sip->si_m_endpos.lnum = 0;
2729071d4279SBram Moolenaar }
2730071d4279SBram Moolenaar sip->si_h_endpos = sip->si_m_endpos;
2731071d4279SBram Moolenaar }
2732071d4279SBram Moolenaar else
2733071d4279SBram Moolenaar {
27340d6f5d97SBram Moolenaar // match within this line
2735071d4279SBram Moolenaar sip->si_m_endpos = endpos;
2736071d4279SBram Moolenaar sip->si_h_endpos = hl_endpos;
2737071d4279SBram Moolenaar sip->si_eoe_pos = end_endpos;
2738071d4279SBram Moolenaar sip->si_ends = TRUE;
2739071d4279SBram Moolenaar sip->si_end_idx = end_idx;
2740071d4279SBram Moolenaar }
2741071d4279SBram Moolenaar }
2742071d4279SBram Moolenaar
2743071d4279SBram Moolenaar /*
2744071d4279SBram Moolenaar * Add a new state to the current state stack.
2745071d4279SBram Moolenaar * It is cleared and the index set to "idx".
2746071d4279SBram Moolenaar * Return FAIL if it's not possible (out of memory).
2747071d4279SBram Moolenaar */
2748071d4279SBram Moolenaar static int
push_current_state(int idx)2749764b23c8SBram Moolenaar push_current_state(int idx)
2750071d4279SBram Moolenaar {
2751071d4279SBram Moolenaar if (ga_grow(¤t_state, 1) == FAIL)
2752071d4279SBram Moolenaar return FAIL;
2753a80faa89SBram Moolenaar CLEAR_POINTER(&CUR_STATE(current_state.ga_len));
2754071d4279SBram Moolenaar CUR_STATE(current_state.ga_len).si_idx = idx;
2755071d4279SBram Moolenaar ++current_state.ga_len;
2756071d4279SBram Moolenaar return OK;
2757071d4279SBram Moolenaar }
2758071d4279SBram Moolenaar
2759071d4279SBram Moolenaar /*
2760071d4279SBram Moolenaar * Remove a state from the current_state stack.
2761071d4279SBram Moolenaar */
2762071d4279SBram Moolenaar static void
pop_current_state(void)2763764b23c8SBram Moolenaar pop_current_state(void)
2764071d4279SBram Moolenaar {
2765071d4279SBram Moolenaar if (current_state.ga_len)
2766071d4279SBram Moolenaar {
2767071d4279SBram Moolenaar unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2768071d4279SBram Moolenaar --current_state.ga_len;
2769071d4279SBram Moolenaar }
27700d6f5d97SBram Moolenaar // after the end of a pattern, try matching a keyword or pattern
2771071d4279SBram Moolenaar next_match_idx = -1;
2772071d4279SBram Moolenaar
27730d6f5d97SBram Moolenaar // if first state with "keepend" is popped, reset keepend_level
2774071d4279SBram Moolenaar if (keepend_level >= current_state.ga_len)
2775071d4279SBram Moolenaar keepend_level = -1;
2776071d4279SBram Moolenaar }
2777071d4279SBram Moolenaar
2778071d4279SBram Moolenaar /*
2779071d4279SBram Moolenaar * Find the end of a start/skip/end syntax region after "startpos".
2780071d4279SBram Moolenaar * Only checks one line.
2781071d4279SBram Moolenaar * Also handles a match item that continued from a previous line.
2782071d4279SBram Moolenaar * If not found, the syntax item continues in the next line. m_endpos->lnum
2783071d4279SBram Moolenaar * will be 0.
2784071d4279SBram Moolenaar * If found, the end of the region and the end of the highlighting is
2785071d4279SBram Moolenaar * computed.
2786071d4279SBram Moolenaar */
2787071d4279SBram Moolenaar static 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)2788764b23c8SBram Moolenaar find_endpos(
27890d6f5d97SBram Moolenaar int idx, // index of the pattern
27900d6f5d97SBram Moolenaar lpos_T *startpos, // where to start looking for an END match
27910d6f5d97SBram Moolenaar lpos_T *m_endpos, // return: end of match
27920d6f5d97SBram Moolenaar lpos_T *hl_endpos, // return: end of highlighting
27930d6f5d97SBram Moolenaar long *flagsp, // return: flags of matching END
27940d6f5d97SBram Moolenaar lpos_T *end_endpos, // return: end of end pattern match
27950d6f5d97SBram Moolenaar int *end_idx, // return: group ID for end pat. match, or 0
27960d6f5d97SBram Moolenaar reg_extmatch_T *start_ext) // submatches from the start pattern
2797071d4279SBram Moolenaar {
2798071d4279SBram Moolenaar colnr_T matchcol;
2799071d4279SBram Moolenaar synpat_T *spp, *spp_skip;
2800071d4279SBram Moolenaar int start_idx;
2801071d4279SBram Moolenaar int best_idx;
2802071d4279SBram Moolenaar regmmatch_T regmatch;
28030d6f5d97SBram Moolenaar regmmatch_T best_regmatch; // startpos/endpos of best match
2804071d4279SBram Moolenaar lpos_T pos;
2805071d4279SBram Moolenaar char_u *line;
2806071d4279SBram Moolenaar int had_match = FALSE;
28070d6f5d97SBram Moolenaar char_u buf_chartab[32]; // chartab array for syn option iskyeyword
2808071d4279SBram Moolenaar
28090d6f5d97SBram Moolenaar // just in case we are invoked for a keyword
28103a36cf7bSBram Moolenaar if (idx < 0)
28113a36cf7bSBram Moolenaar return;
28123a36cf7bSBram Moolenaar
2813071d4279SBram Moolenaar /*
2814071d4279SBram Moolenaar * Check for being called with a START pattern.
2815071d4279SBram Moolenaar * Can happen with a match that continues to the next line, because it
2816071d4279SBram Moolenaar * contained a region.
2817071d4279SBram Moolenaar */
2818860cae1cSBram Moolenaar spp = &(SYN_ITEMS(syn_block)[idx]);
2819071d4279SBram Moolenaar if (spp->sp_type != SPTYPE_START)
2820071d4279SBram Moolenaar {
2821071d4279SBram Moolenaar *hl_endpos = *startpos;
2822071d4279SBram Moolenaar return;
2823071d4279SBram Moolenaar }
2824071d4279SBram Moolenaar
2825071d4279SBram Moolenaar /*
2826071d4279SBram Moolenaar * Find the SKIP or first END pattern after the last START pattern.
2827071d4279SBram Moolenaar */
2828071d4279SBram Moolenaar for (;;)
2829071d4279SBram Moolenaar {
2830860cae1cSBram Moolenaar spp = &(SYN_ITEMS(syn_block)[idx]);
2831071d4279SBram Moolenaar if (spp->sp_type != SPTYPE_START)
2832071d4279SBram Moolenaar break;
2833071d4279SBram Moolenaar ++idx;
2834071d4279SBram Moolenaar }
2835071d4279SBram Moolenaar
2836071d4279SBram Moolenaar /*
2837071d4279SBram Moolenaar * Lookup the SKIP pattern (if present)
2838071d4279SBram Moolenaar */
2839071d4279SBram Moolenaar if (spp->sp_type == SPTYPE_SKIP)
2840071d4279SBram Moolenaar {
2841071d4279SBram Moolenaar spp_skip = spp;
2842071d4279SBram Moolenaar ++idx;
2843071d4279SBram Moolenaar }
2844071d4279SBram Moolenaar else
2845071d4279SBram Moolenaar spp_skip = NULL;
2846071d4279SBram Moolenaar
28470d6f5d97SBram Moolenaar // Setup external matches for syn_regexec().
2848071d4279SBram Moolenaar unref_extmatch(re_extmatch_in);
2849071d4279SBram Moolenaar re_extmatch_in = ref_extmatch(start_ext);
2850071d4279SBram Moolenaar
28510d6f5d97SBram Moolenaar matchcol = startpos->col; // start looking for a match at sstart
28520d6f5d97SBram Moolenaar start_idx = idx; // remember the first END pattern.
28530d6f5d97SBram Moolenaar best_regmatch.startpos[0].col = 0; // avoid compiler warning
2854b8060fe8SBram Moolenaar
28550d6f5d97SBram Moolenaar // use syntax iskeyword option
2856b8060fe8SBram Moolenaar save_chartab(buf_chartab);
2857b8060fe8SBram Moolenaar
2858071d4279SBram Moolenaar for (;;)
2859071d4279SBram Moolenaar {
2860071d4279SBram Moolenaar /*
2861071d4279SBram Moolenaar * Find end pattern that matches first after "matchcol".
2862071d4279SBram Moolenaar */
2863071d4279SBram Moolenaar best_idx = -1;
2864860cae1cSBram Moolenaar for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
2865071d4279SBram Moolenaar {
2866071d4279SBram Moolenaar int lc_col = matchcol;
2867dffa5b8eSBram Moolenaar int r;
2868071d4279SBram Moolenaar
2869860cae1cSBram Moolenaar spp = &(SYN_ITEMS(syn_block)[idx]);
28700d6f5d97SBram Moolenaar if (spp->sp_type != SPTYPE_END) // past last END pattern
2871071d4279SBram Moolenaar break;
2872071d4279SBram Moolenaar lc_col -= spp->sp_offsets[SPO_LC_OFF];
2873071d4279SBram Moolenaar if (lc_col < 0)
2874071d4279SBram Moolenaar lc_col = 0;
2875071d4279SBram Moolenaar
2876071d4279SBram Moolenaar regmatch.rmm_ic = spp->sp_ic;
2877071d4279SBram Moolenaar regmatch.regprog = spp->sp_prog;
2878dffa5b8eSBram Moolenaar r = syn_regexec(®match, startpos->lnum, lc_col,
2879dffa5b8eSBram Moolenaar IF_SYN_TIME(&spp->sp_time));
2880dffa5b8eSBram Moolenaar spp->sp_prog = regmatch.regprog;
2881dffa5b8eSBram Moolenaar if (r)
2882071d4279SBram Moolenaar {
2883071d4279SBram Moolenaar if (best_idx == -1 || regmatch.startpos[0].col
2884071d4279SBram Moolenaar < best_regmatch.startpos[0].col)
2885071d4279SBram Moolenaar {
2886071d4279SBram Moolenaar best_idx = idx;
2887071d4279SBram Moolenaar best_regmatch.startpos[0] = regmatch.startpos[0];
2888071d4279SBram Moolenaar best_regmatch.endpos[0] = regmatch.endpos[0];
2889071d4279SBram Moolenaar }
2890071d4279SBram Moolenaar }
2891071d4279SBram Moolenaar }
2892071d4279SBram Moolenaar
2893071d4279SBram Moolenaar /*
2894071d4279SBram Moolenaar * If all end patterns have been tried, and there is no match, the
2895071d4279SBram Moolenaar * item continues until end-of-line.
2896071d4279SBram Moolenaar */
2897071d4279SBram Moolenaar if (best_idx == -1)
2898071d4279SBram Moolenaar break;
2899071d4279SBram Moolenaar
2900071d4279SBram Moolenaar /*
2901071d4279SBram Moolenaar * If the skip pattern matches before the end pattern,
2902071d4279SBram Moolenaar * continue searching after the skip pattern.
2903071d4279SBram Moolenaar */
2904071d4279SBram Moolenaar if (spp_skip != NULL)
2905071d4279SBram Moolenaar {
2906071d4279SBram Moolenaar int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2907dffa5b8eSBram Moolenaar int r;
2908071d4279SBram Moolenaar
2909071d4279SBram Moolenaar if (lc_col < 0)
2910071d4279SBram Moolenaar lc_col = 0;
2911071d4279SBram Moolenaar regmatch.rmm_ic = spp_skip->sp_ic;
2912071d4279SBram Moolenaar regmatch.regprog = spp_skip->sp_prog;
2913dffa5b8eSBram Moolenaar r = syn_regexec(®match, startpos->lnum, lc_col,
2914dffa5b8eSBram Moolenaar IF_SYN_TIME(&spp_skip->sp_time));
2915dffa5b8eSBram Moolenaar spp_skip->sp_prog = regmatch.regprog;
2916dffa5b8eSBram Moolenaar if (r && regmatch.startpos[0].col
2917071d4279SBram Moolenaar <= best_regmatch.startpos[0].col)
2918071d4279SBram Moolenaar {
291904bff88dSBram Moolenaar int line_len;
292004bff88dSBram Moolenaar
29210d6f5d97SBram Moolenaar // Add offset to skip pattern match
2922071d4279SBram Moolenaar syn_add_end_off(&pos, ®match, spp_skip, SPO_ME_OFF, 1);
2923071d4279SBram Moolenaar
29240d6f5d97SBram Moolenaar // If the skip pattern goes on to the next line, there is no
29250d6f5d97SBram Moolenaar // match with an end pattern in this line.
2926071d4279SBram Moolenaar if (pos.lnum > startpos->lnum)
2927071d4279SBram Moolenaar break;
2928071d4279SBram Moolenaar
2929071d4279SBram Moolenaar line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
293004bff88dSBram Moolenaar line_len = (int)STRLEN(line);
2931071d4279SBram Moolenaar
29320d6f5d97SBram Moolenaar // take care of an empty match or negative offset
2933071d4279SBram Moolenaar if (pos.col <= matchcol)
2934071d4279SBram Moolenaar ++matchcol;
2935071d4279SBram Moolenaar else if (pos.col <= regmatch.endpos[0].col)
2936071d4279SBram Moolenaar matchcol = pos.col;
2937071d4279SBram Moolenaar else
29380d6f5d97SBram Moolenaar // Be careful not to jump over the NUL at the end-of-line
2939071d4279SBram Moolenaar for (matchcol = regmatch.endpos[0].col;
294004bff88dSBram Moolenaar matchcol < line_len && matchcol < pos.col;
2941071d4279SBram Moolenaar ++matchcol)
2942071d4279SBram Moolenaar ;
2943071d4279SBram Moolenaar
29440d6f5d97SBram Moolenaar // if the skip pattern includes end-of-line, break here
294504bff88dSBram Moolenaar if (matchcol >= line_len)
2946071d4279SBram Moolenaar break;
2947071d4279SBram Moolenaar
29480d6f5d97SBram Moolenaar continue; // start with first end pattern again
2949071d4279SBram Moolenaar }
2950071d4279SBram Moolenaar }
2951071d4279SBram Moolenaar
2952071d4279SBram Moolenaar /*
2953071d4279SBram Moolenaar * Match from start pattern to end pattern.
2954071d4279SBram Moolenaar * Correct for match and highlight offset of end pattern.
2955071d4279SBram Moolenaar */
2956860cae1cSBram Moolenaar spp = &(SYN_ITEMS(syn_block)[best_idx]);
2957071d4279SBram Moolenaar syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
29580d6f5d97SBram Moolenaar // can't end before the start
2959071d4279SBram Moolenaar if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
2960071d4279SBram Moolenaar m_endpos->col = startpos->col;
2961071d4279SBram Moolenaar
2962071d4279SBram Moolenaar syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
29630d6f5d97SBram Moolenaar // can't end before the start
2964071d4279SBram Moolenaar if (end_endpos->lnum == startpos->lnum
2965071d4279SBram Moolenaar && end_endpos->col < startpos->col)
2966071d4279SBram Moolenaar end_endpos->col = startpos->col;
29670d6f5d97SBram Moolenaar // can't end after the match
2968071d4279SBram Moolenaar limit_pos(end_endpos, m_endpos);
2969071d4279SBram Moolenaar
2970071d4279SBram Moolenaar /*
2971071d4279SBram Moolenaar * If the end group is highlighted differently, adjust the pointers.
2972071d4279SBram Moolenaar */
2973071d4279SBram Moolenaar if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
2974071d4279SBram Moolenaar {
2975071d4279SBram Moolenaar *end_idx = best_idx;
2976071d4279SBram Moolenaar if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
2977071d4279SBram Moolenaar {
2978071d4279SBram Moolenaar hl_endpos->lnum = best_regmatch.endpos[0].lnum;
2979071d4279SBram Moolenaar hl_endpos->col = best_regmatch.endpos[0].col;
2980071d4279SBram Moolenaar }
2981071d4279SBram Moolenaar else
2982071d4279SBram Moolenaar {
2983071d4279SBram Moolenaar hl_endpos->lnum = best_regmatch.startpos[0].lnum;
2984071d4279SBram Moolenaar hl_endpos->col = best_regmatch.startpos[0].col;
2985071d4279SBram Moolenaar }
2986071d4279SBram Moolenaar hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
2987071d4279SBram Moolenaar
29880d6f5d97SBram Moolenaar // can't end before the start
2989071d4279SBram Moolenaar if (hl_endpos->lnum == startpos->lnum
2990071d4279SBram Moolenaar && hl_endpos->col < startpos->col)
2991071d4279SBram Moolenaar hl_endpos->col = startpos->col;
2992071d4279SBram Moolenaar limit_pos(hl_endpos, m_endpos);
2993071d4279SBram Moolenaar
29940d6f5d97SBram Moolenaar // now the match ends where the highlighting ends, it is turned
29950d6f5d97SBram Moolenaar // into the matchgroup for the end
2996071d4279SBram Moolenaar *m_endpos = *hl_endpos;
2997071d4279SBram Moolenaar }
2998071d4279SBram Moolenaar else
2999071d4279SBram Moolenaar {
3000071d4279SBram Moolenaar *end_idx = 0;
3001071d4279SBram Moolenaar *hl_endpos = *end_endpos;
3002071d4279SBram Moolenaar }
3003071d4279SBram Moolenaar
3004071d4279SBram Moolenaar *flagsp = spp->sp_flags;
3005071d4279SBram Moolenaar
3006071d4279SBram Moolenaar had_match = TRUE;
3007071d4279SBram Moolenaar break;
3008071d4279SBram Moolenaar }
3009071d4279SBram Moolenaar
30100d6f5d97SBram Moolenaar // no match for an END pattern in this line
3011071d4279SBram Moolenaar if (!had_match)
3012071d4279SBram Moolenaar m_endpos->lnum = 0;
3013071d4279SBram Moolenaar
3014b8060fe8SBram Moolenaar restore_chartab(buf_chartab);
3015b8060fe8SBram Moolenaar
30160d6f5d97SBram Moolenaar // Remove external matches.
3017071d4279SBram Moolenaar unref_extmatch(re_extmatch_in);
3018071d4279SBram Moolenaar re_extmatch_in = NULL;
3019071d4279SBram Moolenaar }
3020071d4279SBram Moolenaar
3021071d4279SBram Moolenaar /*
3022071d4279SBram Moolenaar * Limit "pos" not to be after "limit".
3023071d4279SBram Moolenaar */
3024071d4279SBram Moolenaar static void
limit_pos(lpos_T * pos,lpos_T * limit)3025764b23c8SBram Moolenaar limit_pos(lpos_T *pos, lpos_T *limit)
3026071d4279SBram Moolenaar {
3027071d4279SBram Moolenaar if (pos->lnum > limit->lnum)
3028071d4279SBram Moolenaar *pos = *limit;
3029071d4279SBram Moolenaar else if (pos->lnum == limit->lnum && pos->col > limit->col)
3030071d4279SBram Moolenaar pos->col = limit->col;
3031071d4279SBram Moolenaar }
3032071d4279SBram Moolenaar
3033071d4279SBram Moolenaar /*
3034071d4279SBram Moolenaar * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3035071d4279SBram Moolenaar */
3036071d4279SBram Moolenaar static void
limit_pos_zero(lpos_T * pos,lpos_T * limit)3037764b23c8SBram Moolenaar limit_pos_zero(
3038764b23c8SBram Moolenaar lpos_T *pos,
3039764b23c8SBram Moolenaar lpos_T *limit)
3040071d4279SBram Moolenaar {
3041071d4279SBram Moolenaar if (pos->lnum == 0)
3042071d4279SBram Moolenaar *pos = *limit;
3043071d4279SBram Moolenaar else
3044071d4279SBram Moolenaar limit_pos(pos, limit);
3045071d4279SBram Moolenaar }
3046071d4279SBram Moolenaar
3047071d4279SBram Moolenaar /*
3048071d4279SBram Moolenaar * Add offset to matched text for end of match or highlight.
3049071d4279SBram Moolenaar */
3050071d4279SBram Moolenaar static void
syn_add_end_off(lpos_T * result,regmmatch_T * regmatch,synpat_T * spp,int idx,int extra)3051764b23c8SBram Moolenaar syn_add_end_off(
30520d6f5d97SBram Moolenaar lpos_T *result, // returned position
30530d6f5d97SBram Moolenaar regmmatch_T *regmatch, // start/end of match
30540d6f5d97SBram Moolenaar synpat_T *spp, // matched pattern
30550d6f5d97SBram Moolenaar int idx, // index of offset
30560d6f5d97SBram Moolenaar int extra) // extra chars for offset to start
3057071d4279SBram Moolenaar {
3058071d4279SBram Moolenaar int col;
30598c8de839SBram Moolenaar int off;
30608c8de839SBram Moolenaar char_u *base;
30618c8de839SBram Moolenaar char_u *p;
3062071d4279SBram Moolenaar
3063071d4279SBram Moolenaar if (spp->sp_off_flags & (1 << idx))
3064071d4279SBram Moolenaar {
3065071d4279SBram Moolenaar result->lnum = regmatch->startpos[0].lnum;
30668c8de839SBram Moolenaar col = regmatch->startpos[0].col;
30678c8de839SBram Moolenaar off = spp->sp_offsets[idx] + extra;
3068071d4279SBram Moolenaar }
3069071d4279SBram Moolenaar else
3070071d4279SBram Moolenaar {
3071071d4279SBram Moolenaar result->lnum = regmatch->endpos[0].lnum;
3072071d4279SBram Moolenaar col = regmatch->endpos[0].col;
30738c8de839SBram Moolenaar off = spp->sp_offsets[idx];
3074071d4279SBram Moolenaar }
30750d6f5d97SBram Moolenaar // Don't go past the end of the line. Matters for "rs=e+2" when there
30760d6f5d97SBram Moolenaar // is a matchgroup. Watch out for match with last NL in the buffer.
307733aec765SBram Moolenaar if (result->lnum > syn_buf->b_ml.ml_line_count)
30788c8de839SBram Moolenaar col = 0;
30798c8de839SBram Moolenaar else if (off != 0)
30808c8de839SBram Moolenaar {
30818c8de839SBram Moolenaar base = ml_get_buf(syn_buf, result->lnum, FALSE);
30828c8de839SBram Moolenaar p = base + col;
30838c8de839SBram Moolenaar if (off > 0)
30848c8de839SBram Moolenaar {
30858c8de839SBram Moolenaar while (off-- > 0 && *p != NUL)
308691acfffcSBram Moolenaar MB_PTR_ADV(p);
3087071d4279SBram Moolenaar }
30888c8de839SBram Moolenaar else if (off < 0)
30898c8de839SBram Moolenaar {
30908c8de839SBram Moolenaar while (off++ < 0 && base < p)
309191acfffcSBram Moolenaar MB_PTR_BACK(base, p);
30928c8de839SBram Moolenaar }
30938c8de839SBram Moolenaar col = (int)(p - base);
30948c8de839SBram Moolenaar }
30958c8de839SBram Moolenaar result->col = col;
3096a40ceaf8SBram Moolenaar }
3097071d4279SBram Moolenaar
3098071d4279SBram Moolenaar /*
3099071d4279SBram Moolenaar * Add offset to matched text for start of match or highlight.
3100071d4279SBram Moolenaar * Avoid resulting column to become negative.
3101071d4279SBram Moolenaar */
3102071d4279SBram Moolenaar static void
syn_add_start_off(lpos_T * result,regmmatch_T * regmatch,synpat_T * spp,int idx,int extra)3103764b23c8SBram Moolenaar syn_add_start_off(
31040d6f5d97SBram Moolenaar lpos_T *result, // returned position
31050d6f5d97SBram Moolenaar regmmatch_T *regmatch, // start/end of match
3106764b23c8SBram Moolenaar synpat_T *spp,
3107764b23c8SBram Moolenaar int idx,
31080d6f5d97SBram Moolenaar int extra) // extra chars for offset to end
3109071d4279SBram Moolenaar {
3110071d4279SBram Moolenaar int col;
31118c8de839SBram Moolenaar int off;
31128c8de839SBram Moolenaar char_u *base;
31138c8de839SBram Moolenaar char_u *p;
3114071d4279SBram Moolenaar
3115071d4279SBram Moolenaar if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3116071d4279SBram Moolenaar {
3117071d4279SBram Moolenaar result->lnum = regmatch->endpos[0].lnum;
31188c8de839SBram Moolenaar col = regmatch->endpos[0].col;
31198c8de839SBram Moolenaar off = spp->sp_offsets[idx] + extra;
3120071d4279SBram Moolenaar }
3121071d4279SBram Moolenaar else
3122071d4279SBram Moolenaar {
3123071d4279SBram Moolenaar result->lnum = regmatch->startpos[0].lnum;
3124071d4279SBram Moolenaar col = regmatch->startpos[0].col;
31258c8de839SBram Moolenaar off = spp->sp_offsets[idx];
3126071d4279SBram Moolenaar }
312772b73c12SBram Moolenaar if (result->lnum > syn_buf->b_ml.ml_line_count)
312872b73c12SBram Moolenaar {
31290d6f5d97SBram Moolenaar // a "\n" at the end of the pattern may take us below the last line
313072b73c12SBram Moolenaar result->lnum = syn_buf->b_ml.ml_line_count;
31318b9c05faSBram Moolenaar col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
313272b73c12SBram Moolenaar }
31338c8de839SBram Moolenaar if (off != 0)
31348c8de839SBram Moolenaar {
31358c8de839SBram Moolenaar base = ml_get_buf(syn_buf, result->lnum, FALSE);
31368c8de839SBram Moolenaar p = base + col;
31378c8de839SBram Moolenaar if (off > 0)
31388c8de839SBram Moolenaar {
31398c8de839SBram Moolenaar while (off-- && *p != NUL)
314091acfffcSBram Moolenaar MB_PTR_ADV(p);
31418c8de839SBram Moolenaar }
31428c8de839SBram Moolenaar else if (off < 0)
31438c8de839SBram Moolenaar {
31448c8de839SBram Moolenaar while (off++ && base < p)
314591acfffcSBram Moolenaar MB_PTR_BACK(base, p);
31468c8de839SBram Moolenaar }
31478c8de839SBram Moolenaar col = (int)(p - base);
31488c8de839SBram Moolenaar }
3149071d4279SBram Moolenaar result->col = col;
3150071d4279SBram Moolenaar }
3151071d4279SBram Moolenaar
3152071d4279SBram Moolenaar /*
3153071d4279SBram Moolenaar * Get current line in syntax buffer.
3154071d4279SBram Moolenaar */
3155071d4279SBram Moolenaar static char_u *
syn_getcurline(void)3156764b23c8SBram Moolenaar syn_getcurline(void)
3157071d4279SBram Moolenaar {
3158071d4279SBram Moolenaar return ml_get_buf(syn_buf, current_lnum, FALSE);
3159071d4279SBram Moolenaar }
3160071d4279SBram Moolenaar
3161071d4279SBram Moolenaar /*
31623b56eb3dSBram Moolenaar * Call vim_regexec() to find a match with "rmp" in "syn_buf".
3163071d4279SBram Moolenaar * Returns TRUE when there is a match.
3164071d4279SBram Moolenaar */
3165071d4279SBram Moolenaar static int
syn_regexec(regmmatch_T * rmp,linenr_T lnum,colnr_T col,syn_time_T * st UNUSED)3166764b23c8SBram Moolenaar syn_regexec(
3167764b23c8SBram Moolenaar regmmatch_T *rmp,
3168764b23c8SBram Moolenaar linenr_T lnum,
3169764b23c8SBram Moolenaar colnr_T col,
3170764b23c8SBram Moolenaar syn_time_T *st UNUSED)
3171071d4279SBram Moolenaar {
31728a7f5a2dSBram Moolenaar int r;
317306f1ed2fSBram Moolenaar #ifdef FEAT_RELTIME
317406f1ed2fSBram Moolenaar int timed_out = FALSE;
317506f1ed2fSBram Moolenaar #endif
3176f7512552SBram Moolenaar #ifdef FEAT_PROFILE
31778a7f5a2dSBram Moolenaar proftime_T pt;
31788a7f5a2dSBram Moolenaar
31798a7f5a2dSBram Moolenaar if (syn_time_on)
31808a7f5a2dSBram Moolenaar profile_start(&pt);
31818a7f5a2dSBram Moolenaar #endif
31828a7f5a2dSBram Moolenaar
3183bcf94423SBram Moolenaar if (rmp->regprog == NULL)
3184bcf94423SBram Moolenaar // This can happen if a previous call to vim_regexec_multi() tried to
3185bcf94423SBram Moolenaar // use the NFA engine, which resulted in NFA_TOO_EXPENSIVE, and
3186bcf94423SBram Moolenaar // compiling the pattern with the other engine fails.
3187bcf94423SBram Moolenaar return FALSE;
3188bcf94423SBram Moolenaar
31893b56eb3dSBram Moolenaar rmp->rmm_maxcol = syn_buf->b_p_smc;
319006f1ed2fSBram Moolenaar r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
319106f1ed2fSBram Moolenaar #ifdef FEAT_RELTIME
319206f1ed2fSBram Moolenaar syn_tm, &timed_out
319306f1ed2fSBram Moolenaar #else
319406f1ed2fSBram Moolenaar NULL, NULL
319506f1ed2fSBram Moolenaar #endif
319606f1ed2fSBram Moolenaar );
31978a7f5a2dSBram Moolenaar
3198f7512552SBram Moolenaar #ifdef FEAT_PROFILE
31998a7f5a2dSBram Moolenaar if (syn_time_on)
32008a7f5a2dSBram Moolenaar {
32018a7f5a2dSBram Moolenaar profile_end(&pt);
32028a7f5a2dSBram Moolenaar profile_add(&st->total, &pt);
32038a7f5a2dSBram Moolenaar if (profile_cmp(&pt, &st->slowest) < 0)
32048a7f5a2dSBram Moolenaar st->slowest = pt;
32058a7f5a2dSBram Moolenaar ++st->count;
32068a7f5a2dSBram Moolenaar if (r > 0)
32078a7f5a2dSBram Moolenaar ++st->match;
32088a7f5a2dSBram Moolenaar }
32098a7f5a2dSBram Moolenaar #endif
321006f1ed2fSBram Moolenaar #ifdef FEAT_RELTIME
32110a6efcd2SBram Moolenaar if (timed_out && !syn_win->w_s->b_syn_slow)
32120a6efcd2SBram Moolenaar {
321306f1ed2fSBram Moolenaar syn_win->w_s->b_syn_slow = TRUE;
321432526b3cSBram Moolenaar msg(_("'redrawtime' exceeded, syntax highlighting disabled"));
32150a6efcd2SBram Moolenaar }
321606f1ed2fSBram Moolenaar #endif
32178a7f5a2dSBram Moolenaar
32188a7f5a2dSBram Moolenaar if (r > 0)
3219071d4279SBram Moolenaar {
3220071d4279SBram Moolenaar rmp->startpos[0].lnum += lnum;
3221071d4279SBram Moolenaar rmp->endpos[0].lnum += lnum;
3222071d4279SBram Moolenaar return TRUE;
3223071d4279SBram Moolenaar }
3224071d4279SBram Moolenaar return FALSE;
3225071d4279SBram Moolenaar }
3226071d4279SBram Moolenaar
3227071d4279SBram Moolenaar /*
3228071d4279SBram Moolenaar * Check one position in a line for a matching keyword.
3229071d4279SBram Moolenaar * The caller must check if a keyword can start at startcol.
3230aab93b12SBram Moolenaar * Return its ID if found, 0 otherwise.
3231071d4279SBram Moolenaar */
3232071d4279SBram Moolenaar static int
check_keyword_id(char_u * line,int startcol,int * endcolp,long * flagsp,short ** next_listp,stateitem_T * cur_si,int * ccharp UNUSED)3233764b23c8SBram Moolenaar check_keyword_id(
3234764b23c8SBram Moolenaar char_u *line,
32350d6f5d97SBram Moolenaar int startcol, // position in line to check for keyword
32360d6f5d97SBram Moolenaar int *endcolp, // return: character after found keyword
32370d6f5d97SBram Moolenaar long *flagsp, // return: flags of matching keyword
32380d6f5d97SBram Moolenaar short **next_listp, // return: next_list of matching keyword
32390d6f5d97SBram Moolenaar stateitem_T *cur_si, // item at the top of the stack
32400d6f5d97SBram Moolenaar int *ccharp UNUSED) // conceal substitution char
3241071d4279SBram Moolenaar {
3242dad6b69cSBram Moolenaar keyentry_T *kp;
3243dad6b69cSBram Moolenaar char_u *kwp;
3244071d4279SBram Moolenaar int round;
3245dad6b69cSBram Moolenaar int kwlen;
32460d6f5d97SBram Moolenaar char_u keyword[MAXKEYWLEN + 1]; // assume max. keyword len is 80
3247dad6b69cSBram Moolenaar hashtab_T *ht;
3248dad6b69cSBram Moolenaar hashitem_T *hi;
3249071d4279SBram Moolenaar
32500d6f5d97SBram Moolenaar // Find first character after the keyword. First character was already
32510d6f5d97SBram Moolenaar // checked.
3252dad6b69cSBram Moolenaar kwp = line + startcol;
3253dad6b69cSBram Moolenaar kwlen = 0;
3254071d4279SBram Moolenaar do
3255071d4279SBram Moolenaar {
3256071d4279SBram Moolenaar if (has_mbyte)
32570fa313a7SBram Moolenaar kwlen += (*mb_ptr2len)(kwp + kwlen);
3258071d4279SBram Moolenaar else
3259dad6b69cSBram Moolenaar ++kwlen;
3260071d4279SBram Moolenaar }
32619d182dd0SBram Moolenaar while (vim_iswordp_buf(kwp + kwlen, syn_buf));
3262071d4279SBram Moolenaar
3263dad6b69cSBram Moolenaar if (kwlen > MAXKEYWLEN)
3264071d4279SBram Moolenaar return 0;
3265071d4279SBram Moolenaar
3266071d4279SBram Moolenaar /*
3267071d4279SBram Moolenaar * Must make a copy of the keyword, so we can add a NUL and make it
3268071d4279SBram Moolenaar * lowercase.
3269071d4279SBram Moolenaar */
3270ce0842a6SBram Moolenaar vim_strncpy(keyword, kwp, kwlen);
3271071d4279SBram Moolenaar
3272071d4279SBram Moolenaar /*
3273071d4279SBram Moolenaar * Try twice:
3274071d4279SBram Moolenaar * 1. matching case
3275071d4279SBram Moolenaar * 2. ignoring case
3276071d4279SBram Moolenaar */
3277071d4279SBram Moolenaar for (round = 1; round <= 2; ++round)
3278071d4279SBram Moolenaar {
3279860cae1cSBram Moolenaar ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
3280dad6b69cSBram Moolenaar if (ht->ht_used == 0)
3281071d4279SBram Moolenaar continue;
32820d6f5d97SBram Moolenaar if (round == 2) // ignore case
3283dad6b69cSBram Moolenaar (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
3284071d4279SBram Moolenaar
3285071d4279SBram Moolenaar /*
3286dad6b69cSBram Moolenaar * Find keywords that match. There can be several with different
3287dad6b69cSBram Moolenaar * attributes.
3288071d4279SBram Moolenaar * When current_next_list is non-zero accept only that group, otherwise:
3289071d4279SBram Moolenaar * Accept a not-contained keyword at toplevel.
3290071d4279SBram Moolenaar * Accept a keyword at other levels only if it is in the contains list.
3291071d4279SBram Moolenaar */
3292dad6b69cSBram Moolenaar hi = hash_find(ht, keyword);
3293dad6b69cSBram Moolenaar if (!HASHITEM_EMPTY(hi))
3294dad6b69cSBram Moolenaar for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
3295071d4279SBram Moolenaar {
3296dad6b69cSBram Moolenaar if (current_next_list != 0
3297dad6b69cSBram Moolenaar ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3298dad6b69cSBram Moolenaar : (cur_si == NULL
3299dad6b69cSBram Moolenaar ? !(kp->flags & HL_CONTAINED)
3300dad6b69cSBram Moolenaar : in_id_list(cur_si, cur_si->si_cont_list,
3301dad6b69cSBram Moolenaar &kp->k_syn, kp->flags & HL_CONTAINED)))
3302dad6b69cSBram Moolenaar {
3303dad6b69cSBram Moolenaar *endcolp = startcol + kwlen;
3304dad6b69cSBram Moolenaar *flagsp = kp->flags;
3305dad6b69cSBram Moolenaar *next_listp = kp->next_list;
3306860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
3307860cae1cSBram Moolenaar *ccharp = kp->k_char;
3308860cae1cSBram Moolenaar #endif
3309dad6b69cSBram Moolenaar return kp->k_syn.id;
3310dad6b69cSBram Moolenaar }
3311071d4279SBram Moolenaar }
3312071d4279SBram Moolenaar }
3313071d4279SBram Moolenaar return 0;
3314071d4279SBram Moolenaar }
3315071d4279SBram Moolenaar
3316071d4279SBram Moolenaar /*
3317860cae1cSBram Moolenaar * Handle ":syntax conceal" command.
3318860cae1cSBram Moolenaar */
3319860cae1cSBram Moolenaar static void
syn_cmd_conceal(exarg_T * eap UNUSED,int syncing UNUSED)3320764b23c8SBram Moolenaar syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
3321860cae1cSBram Moolenaar {
3322860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
3323860cae1cSBram Moolenaar char_u *arg = eap->arg;
3324860cae1cSBram Moolenaar char_u *next;
3325860cae1cSBram Moolenaar
3326860cae1cSBram Moolenaar eap->nextcmd = find_nextcmd(arg);
3327860cae1cSBram Moolenaar if (eap->skip)
3328860cae1cSBram Moolenaar return;
3329860cae1cSBram Moolenaar
3330860cae1cSBram Moolenaar next = skiptowhite(arg);
3331de318c5cSBram Moolenaar if (*arg == NUL)
3332de318c5cSBram Moolenaar {
3333de318c5cSBram Moolenaar if (curwin->w_s->b_syn_conceal)
333432526b3cSBram Moolenaar msg(_("syntax conceal on"));
3335de318c5cSBram Moolenaar else
333632526b3cSBram Moolenaar msg(_("syntax conceal off"));
3337de318c5cSBram Moolenaar }
3338de318c5cSBram Moolenaar else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3339860cae1cSBram Moolenaar curwin->w_s->b_syn_conceal = TRUE;
3340860cae1cSBram Moolenaar else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3341860cae1cSBram Moolenaar curwin->w_s->b_syn_conceal = FALSE;
3342860cae1cSBram Moolenaar else
3343e35a52aeSBram Moolenaar semsg(_(e_illegal_arg), arg);
3344860cae1cSBram Moolenaar #endif
3345860cae1cSBram Moolenaar }
3346860cae1cSBram Moolenaar
3347860cae1cSBram Moolenaar /*
3348071d4279SBram Moolenaar * Handle ":syntax case" command.
3349071d4279SBram Moolenaar */
3350071d4279SBram Moolenaar static void
syn_cmd_case(exarg_T * eap,int syncing UNUSED)3351764b23c8SBram Moolenaar syn_cmd_case(exarg_T *eap, int syncing UNUSED)
3352071d4279SBram Moolenaar {
3353071d4279SBram Moolenaar char_u *arg = eap->arg;
3354071d4279SBram Moolenaar char_u *next;
3355071d4279SBram Moolenaar
3356071d4279SBram Moolenaar eap->nextcmd = find_nextcmd(arg);
3357071d4279SBram Moolenaar if (eap->skip)
3358071d4279SBram Moolenaar return;
3359071d4279SBram Moolenaar
3360071d4279SBram Moolenaar next = skiptowhite(arg);
3361de318c5cSBram Moolenaar if (*arg == NUL)
3362de318c5cSBram Moolenaar {
3363de318c5cSBram Moolenaar if (curwin->w_s->b_syn_ic)
336432526b3cSBram Moolenaar msg(_("syntax case ignore"));
3365de318c5cSBram Moolenaar else
336632526b3cSBram Moolenaar msg(_("syntax case match"));
3367de318c5cSBram Moolenaar }
3368de318c5cSBram Moolenaar else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
3369860cae1cSBram Moolenaar curwin->w_s->b_syn_ic = FALSE;
3370071d4279SBram Moolenaar else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
3371860cae1cSBram Moolenaar curwin->w_s->b_syn_ic = TRUE;
3372071d4279SBram Moolenaar else
3373e35a52aeSBram Moolenaar semsg(_(e_illegal_arg), arg);
3374e35a52aeSBram Moolenaar }
3375e35a52aeSBram Moolenaar
3376e35a52aeSBram Moolenaar /*
3377e35a52aeSBram Moolenaar * Handle ":syntax foldlevel" command.
3378e35a52aeSBram Moolenaar */
3379e35a52aeSBram Moolenaar static void
syn_cmd_foldlevel(exarg_T * eap,int syncing UNUSED)3380e35a52aeSBram Moolenaar syn_cmd_foldlevel(exarg_T *eap, int syncing UNUSED)
3381e35a52aeSBram Moolenaar {
3382e35a52aeSBram Moolenaar char_u *arg = eap->arg;
3383e35a52aeSBram Moolenaar char_u *arg_end;
3384e35a52aeSBram Moolenaar
3385e35a52aeSBram Moolenaar eap->nextcmd = find_nextcmd(arg);
3386e35a52aeSBram Moolenaar if (eap->skip)
3387e35a52aeSBram Moolenaar return;
3388e35a52aeSBram Moolenaar
3389e35a52aeSBram Moolenaar if (*arg == NUL)
3390e35a52aeSBram Moolenaar {
3391e35a52aeSBram Moolenaar switch (curwin->w_s->b_syn_foldlevel)
3392e35a52aeSBram Moolenaar {
3393e35a52aeSBram Moolenaar case SYNFLD_START: msg(_("syntax foldlevel start")); break;
3394e35a52aeSBram Moolenaar case SYNFLD_MINIMUM: msg(_("syntax foldlevel minimum")); break;
3395e35a52aeSBram Moolenaar default: break;
3396e35a52aeSBram Moolenaar }
3397e35a52aeSBram Moolenaar return;
3398e35a52aeSBram Moolenaar }
3399e35a52aeSBram Moolenaar
3400e35a52aeSBram Moolenaar arg_end = skiptowhite(arg);
3401e35a52aeSBram Moolenaar if (STRNICMP(arg, "start", 5) == 0 && arg_end - arg == 5)
3402e35a52aeSBram Moolenaar curwin->w_s->b_syn_foldlevel = SYNFLD_START;
3403e35a52aeSBram Moolenaar else if (STRNICMP(arg, "minimum", 7) == 0 && arg_end - arg == 7)
3404e35a52aeSBram Moolenaar curwin->w_s->b_syn_foldlevel = SYNFLD_MINIMUM;
3405e35a52aeSBram Moolenaar else
3406e35a52aeSBram Moolenaar {
3407e35a52aeSBram Moolenaar semsg(_(e_illegal_arg), arg);
3408e35a52aeSBram Moolenaar return;
3409e35a52aeSBram Moolenaar }
3410e35a52aeSBram Moolenaar
3411e35a52aeSBram Moolenaar arg = skipwhite(arg_end);
3412e35a52aeSBram Moolenaar if (*arg != NUL)
3413e35a52aeSBram Moolenaar {
3414e35a52aeSBram Moolenaar semsg(_(e_illegal_arg), arg);
3415e35a52aeSBram Moolenaar }
3416071d4279SBram Moolenaar }
3417071d4279SBram Moolenaar
3418071d4279SBram Moolenaar /*
3419ce0842a6SBram Moolenaar * Handle ":syntax spell" command.
3420ce0842a6SBram Moolenaar */
3421ce0842a6SBram Moolenaar static void
syn_cmd_spell(exarg_T * eap,int syncing UNUSED)3422764b23c8SBram Moolenaar syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
3423ce0842a6SBram Moolenaar {
3424ce0842a6SBram Moolenaar char_u *arg = eap->arg;
3425ce0842a6SBram Moolenaar char_u *next;
3426ce0842a6SBram Moolenaar
3427ce0842a6SBram Moolenaar eap->nextcmd = find_nextcmd(arg);
3428ce0842a6SBram Moolenaar if (eap->skip)
3429ce0842a6SBram Moolenaar return;
3430ce0842a6SBram Moolenaar
3431ce0842a6SBram Moolenaar next = skiptowhite(arg);
3432de318c5cSBram Moolenaar if (*arg == NUL)
3433de318c5cSBram Moolenaar {
3434de318c5cSBram Moolenaar if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
343532526b3cSBram Moolenaar msg(_("syntax spell toplevel"));
3436de318c5cSBram Moolenaar else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
343732526b3cSBram Moolenaar msg(_("syntax spell notoplevel"));
3438de318c5cSBram Moolenaar else
343932526b3cSBram Moolenaar msg(_("syntax spell default"));
3440de318c5cSBram Moolenaar }
3441de318c5cSBram Moolenaar else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
3442860cae1cSBram Moolenaar curwin->w_s->b_syn_spell = SYNSPL_TOP;
3443ce0842a6SBram Moolenaar else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
3444860cae1cSBram Moolenaar curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
344586ea7640SBram Moolenaar else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
3446860cae1cSBram Moolenaar curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
3447ce0842a6SBram Moolenaar else
34485081d202SBram Moolenaar {
3449e35a52aeSBram Moolenaar semsg(_(e_illegal_arg), arg);
34505081d202SBram Moolenaar return;
34515081d202SBram Moolenaar }
34525081d202SBram Moolenaar
34530d6f5d97SBram Moolenaar // assume spell checking changed, force a redraw
34545081d202SBram Moolenaar redraw_win_later(curwin, NOT_VALID);
3455ce0842a6SBram Moolenaar }
3456ce0842a6SBram Moolenaar
3457ce0842a6SBram Moolenaar /*
3458b8060fe8SBram Moolenaar * Handle ":syntax iskeyword" command.
3459b8060fe8SBram Moolenaar */
3460b8060fe8SBram Moolenaar static void
syn_cmd_iskeyword(exarg_T * eap,int syncing UNUSED)3461764b23c8SBram Moolenaar syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
3462b8060fe8SBram Moolenaar {
3463b8060fe8SBram Moolenaar char_u *arg = eap->arg;
3464b8060fe8SBram Moolenaar char_u save_chartab[32];
3465b8060fe8SBram Moolenaar char_u *save_isk;
3466b8060fe8SBram Moolenaar
3467b8060fe8SBram Moolenaar if (eap->skip)
3468b8060fe8SBram Moolenaar return;
3469b8060fe8SBram Moolenaar
3470b8060fe8SBram Moolenaar arg = skipwhite(arg);
3471b8060fe8SBram Moolenaar if (*arg == NUL)
3472b8060fe8SBram Moolenaar {
347332526b3cSBram Moolenaar msg_puts("\n");
3474b8060fe8SBram Moolenaar if (curwin->w_s->b_syn_isk != empty_option)
34750a6efcd2SBram Moolenaar {
347632526b3cSBram Moolenaar msg_puts(_("syntax iskeyword "));
3477b8060fe8SBram Moolenaar msg_outtrans(curwin->w_s->b_syn_isk);
34780a6efcd2SBram Moolenaar }
3479b8060fe8SBram Moolenaar else
34800a6efcd2SBram Moolenaar msg_outtrans((char_u *)_("syntax iskeyword not set"));
3481b8060fe8SBram Moolenaar }
3482b8060fe8SBram Moolenaar else
3483b8060fe8SBram Moolenaar {
3484b8060fe8SBram Moolenaar if (STRNICMP(arg, "clear", 5) == 0)
3485b8060fe8SBram Moolenaar {
3486b8060fe8SBram Moolenaar mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3487b8060fe8SBram Moolenaar (size_t)32);
3488b8060fe8SBram Moolenaar clear_string_option(&curwin->w_s->b_syn_isk);
3489b8060fe8SBram Moolenaar }
3490b8060fe8SBram Moolenaar else
3491b8060fe8SBram Moolenaar {
3492b8060fe8SBram Moolenaar mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3493b8060fe8SBram Moolenaar save_isk = curbuf->b_p_isk;
3494b8060fe8SBram Moolenaar curbuf->b_p_isk = vim_strsave(arg);
3495b8060fe8SBram Moolenaar
3496b8060fe8SBram Moolenaar buf_init_chartab(curbuf, FALSE);
3497b8060fe8SBram Moolenaar mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3498b8060fe8SBram Moolenaar (size_t)32);
3499b8060fe8SBram Moolenaar mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3500b8060fe8SBram Moolenaar clear_string_option(&curwin->w_s->b_syn_isk);
3501b8060fe8SBram Moolenaar curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3502b8060fe8SBram Moolenaar curbuf->b_p_isk = save_isk;
3503b8060fe8SBram Moolenaar }
3504b8060fe8SBram Moolenaar }
3505b8060fe8SBram Moolenaar redraw_win_later(curwin, NOT_VALID);
3506b8060fe8SBram Moolenaar }
3507b8060fe8SBram Moolenaar
3508b8060fe8SBram Moolenaar /*
3509071d4279SBram Moolenaar * Clear all syntax info for one buffer.
3510071d4279SBram Moolenaar */
3511071d4279SBram Moolenaar void
syntax_clear(synblock_T * block)3512764b23c8SBram Moolenaar syntax_clear(synblock_T *block)
3513071d4279SBram Moolenaar {
3514071d4279SBram Moolenaar int i;
3515071d4279SBram Moolenaar
35160d6f5d97SBram Moolenaar block->b_syn_error = FALSE; // clear previous error
351706f1ed2fSBram Moolenaar #ifdef FEAT_RELTIME
35180d6f5d97SBram Moolenaar block->b_syn_slow = FALSE; // clear previous timeout
351906f1ed2fSBram Moolenaar #endif
35200d6f5d97SBram Moolenaar block->b_syn_ic = FALSE; // Use case, by default
3521e35a52aeSBram Moolenaar block->b_syn_foldlevel = SYNFLD_START;
35220d6f5d97SBram Moolenaar block->b_syn_spell = SYNSPL_DEFAULT; // default spell checking
3523860cae1cSBram Moolenaar block->b_syn_containedin = FALSE;
3524de318c5cSBram Moolenaar #ifdef FEAT_CONCEAL
3525de318c5cSBram Moolenaar block->b_syn_conceal = FALSE;
3526de318c5cSBram Moolenaar #endif
3527071d4279SBram Moolenaar
35280d6f5d97SBram Moolenaar // free the keywords
3529860cae1cSBram Moolenaar clear_keywtab(&block->b_keywtab);
3530860cae1cSBram Moolenaar clear_keywtab(&block->b_keywtab_ic);
3531071d4279SBram Moolenaar
35320d6f5d97SBram Moolenaar // free the syntax patterns
3533860cae1cSBram Moolenaar for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3534860cae1cSBram Moolenaar syn_clear_pattern(block, i);
3535860cae1cSBram Moolenaar ga_clear(&block->b_syn_patterns);
3536071d4279SBram Moolenaar
35370d6f5d97SBram Moolenaar // free the syntax clusters
3538860cae1cSBram Moolenaar for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3539860cae1cSBram Moolenaar syn_clear_cluster(block, i);
3540860cae1cSBram Moolenaar ga_clear(&block->b_syn_clusters);
3541860cae1cSBram Moolenaar block->b_spell_cluster_id = 0;
3542860cae1cSBram Moolenaar block->b_nospell_cluster_id = 0;
3543071d4279SBram Moolenaar
3544860cae1cSBram Moolenaar block->b_syn_sync_flags = 0;
3545860cae1cSBram Moolenaar block->b_syn_sync_minlines = 0;
3546860cae1cSBram Moolenaar block->b_syn_sync_maxlines = 0;
3547860cae1cSBram Moolenaar block->b_syn_sync_linebreaks = 0;
3548071d4279SBram Moolenaar
3549473de61bSBram Moolenaar vim_regfree(block->b_syn_linecont_prog);
3550860cae1cSBram Moolenaar block->b_syn_linecont_prog = NULL;
3551d23a8236SBram Moolenaar VIM_CLEAR(block->b_syn_linecont_pat);
3552071d4279SBram Moolenaar #ifdef FEAT_FOLDING
3553860cae1cSBram Moolenaar block->b_syn_folditems = 0;
3554071d4279SBram Moolenaar #endif
3555b8060fe8SBram Moolenaar clear_string_option(&block->b_syn_isk);
3556071d4279SBram Moolenaar
35570d6f5d97SBram Moolenaar // free the stored states
3558860cae1cSBram Moolenaar syn_stack_free_all(block);
3559071d4279SBram Moolenaar invalidate_current_state();
356042431a7aSBram Moolenaar
35610d6f5d97SBram Moolenaar // Reset the counter for ":syn include"
356242431a7aSBram Moolenaar running_syn_inc_tag = 0;
3563071d4279SBram Moolenaar }
3564071d4279SBram Moolenaar
3565071d4279SBram Moolenaar /*
3566fd29f462SBram Moolenaar * Get rid of ownsyntax for window "wp".
3567fd29f462SBram Moolenaar */
3568fd29f462SBram Moolenaar void
reset_synblock(win_T * wp)3569764b23c8SBram Moolenaar reset_synblock(win_T *wp)
3570fd29f462SBram Moolenaar {
3571fd29f462SBram Moolenaar if (wp->w_s != &wp->w_buffer->b_s)
3572fd29f462SBram Moolenaar {
3573fd29f462SBram Moolenaar syntax_clear(wp->w_s);
3574fd29f462SBram Moolenaar vim_free(wp->w_s);
3575fd29f462SBram Moolenaar wp->w_s = &wp->w_buffer->b_s;
3576fd29f462SBram Moolenaar }
3577fd29f462SBram Moolenaar }
3578fd29f462SBram Moolenaar
3579fd29f462SBram Moolenaar /*
3580071d4279SBram Moolenaar * Clear syncing info for one buffer.
3581071d4279SBram Moolenaar */
3582071d4279SBram Moolenaar static void
syntax_sync_clear(void)3583764b23c8SBram Moolenaar syntax_sync_clear(void)
3584071d4279SBram Moolenaar {
3585071d4279SBram Moolenaar int i;
3586071d4279SBram Moolenaar
35870d6f5d97SBram Moolenaar // free the syntax patterns
3588860cae1cSBram Moolenaar for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3589860cae1cSBram Moolenaar if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3590860cae1cSBram Moolenaar syn_remove_pattern(curwin->w_s, i);
3591071d4279SBram Moolenaar
3592860cae1cSBram Moolenaar curwin->w_s->b_syn_sync_flags = 0;
3593860cae1cSBram Moolenaar curwin->w_s->b_syn_sync_minlines = 0;
3594860cae1cSBram Moolenaar curwin->w_s->b_syn_sync_maxlines = 0;
3595860cae1cSBram Moolenaar curwin->w_s->b_syn_sync_linebreaks = 0;
3596071d4279SBram Moolenaar
3597473de61bSBram Moolenaar vim_regfree(curwin->w_s->b_syn_linecont_prog);
3598860cae1cSBram Moolenaar curwin->w_s->b_syn_linecont_prog = NULL;
3599d23a8236SBram Moolenaar VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
3600b8060fe8SBram Moolenaar clear_string_option(&curwin->w_s->b_syn_isk);
3601071d4279SBram Moolenaar
36020d6f5d97SBram Moolenaar syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
3603071d4279SBram Moolenaar }
3604071d4279SBram Moolenaar
3605071d4279SBram Moolenaar /*
3606071d4279SBram Moolenaar * Remove one pattern from the buffer's pattern list.
3607071d4279SBram Moolenaar */
3608071d4279SBram Moolenaar static void
syn_remove_pattern(synblock_T * block,int idx)3609764b23c8SBram Moolenaar syn_remove_pattern(
3610764b23c8SBram Moolenaar synblock_T *block,
3611764b23c8SBram Moolenaar int idx)
3612071d4279SBram Moolenaar {
3613071d4279SBram Moolenaar synpat_T *spp;
3614071d4279SBram Moolenaar
3615860cae1cSBram Moolenaar spp = &(SYN_ITEMS(block)[idx]);
3616071d4279SBram Moolenaar #ifdef FEAT_FOLDING
3617071d4279SBram Moolenaar if (spp->sp_flags & HL_FOLD)
3618860cae1cSBram Moolenaar --block->b_syn_folditems;
3619071d4279SBram Moolenaar #endif
3620860cae1cSBram Moolenaar syn_clear_pattern(block, idx);
3621071d4279SBram Moolenaar mch_memmove(spp, spp + 1,
3622860cae1cSBram Moolenaar sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3623860cae1cSBram Moolenaar --block->b_syn_patterns.ga_len;
3624071d4279SBram Moolenaar }
3625071d4279SBram Moolenaar
3626071d4279SBram Moolenaar /*
3627071d4279SBram Moolenaar * Clear and free one syntax pattern. When clearing all, must be called from
3628071d4279SBram Moolenaar * last to first!
3629071d4279SBram Moolenaar */
3630071d4279SBram Moolenaar static void
syn_clear_pattern(synblock_T * block,int i)3631764b23c8SBram Moolenaar syn_clear_pattern(synblock_T *block, int i)
3632071d4279SBram Moolenaar {
3633860cae1cSBram Moolenaar vim_free(SYN_ITEMS(block)[i].sp_pattern);
3634473de61bSBram Moolenaar vim_regfree(SYN_ITEMS(block)[i].sp_prog);
36350d6f5d97SBram Moolenaar // Only free sp_cont_list and sp_next_list of first start pattern
3636860cae1cSBram Moolenaar if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
3637071d4279SBram Moolenaar {
3638860cae1cSBram Moolenaar vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3639860cae1cSBram Moolenaar vim_free(SYN_ITEMS(block)[i].sp_next_list);
3640860cae1cSBram Moolenaar vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
3641071d4279SBram Moolenaar }
3642071d4279SBram Moolenaar }
3643071d4279SBram Moolenaar
3644071d4279SBram Moolenaar /*
3645071d4279SBram Moolenaar * Clear and free one syntax cluster.
3646071d4279SBram Moolenaar */
3647071d4279SBram Moolenaar static void
syn_clear_cluster(synblock_T * block,int i)3648764b23c8SBram Moolenaar syn_clear_cluster(synblock_T *block, int i)
3649071d4279SBram Moolenaar {
3650860cae1cSBram Moolenaar vim_free(SYN_CLSTR(block)[i].scl_name);
3651860cae1cSBram Moolenaar vim_free(SYN_CLSTR(block)[i].scl_name_u);
3652860cae1cSBram Moolenaar vim_free(SYN_CLSTR(block)[i].scl_list);
3653071d4279SBram Moolenaar }
3654071d4279SBram Moolenaar
3655071d4279SBram Moolenaar /*
3656071d4279SBram Moolenaar * Handle ":syntax clear" command.
3657071d4279SBram Moolenaar */
3658071d4279SBram Moolenaar static void
syn_cmd_clear(exarg_T * eap,int syncing)3659764b23c8SBram Moolenaar syn_cmd_clear(exarg_T *eap, int syncing)
3660071d4279SBram Moolenaar {
3661071d4279SBram Moolenaar char_u *arg = eap->arg;
3662071d4279SBram Moolenaar char_u *arg_end;
3663071d4279SBram Moolenaar int id;
3664071d4279SBram Moolenaar
3665071d4279SBram Moolenaar eap->nextcmd = find_nextcmd(arg);
3666071d4279SBram Moolenaar if (eap->skip)
3667071d4279SBram Moolenaar return;
3668071d4279SBram Moolenaar
3669071d4279SBram Moolenaar /*
3670071d4279SBram Moolenaar * We have to disable this within ":syn include @group filename",
3671071d4279SBram Moolenaar * because otherwise @group would get deleted.
3672071d4279SBram Moolenaar * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3673071d4279SBram Moolenaar * clear".
3674071d4279SBram Moolenaar */
3675860cae1cSBram Moolenaar if (curwin->w_s->b_syn_topgrp != 0)
3676071d4279SBram Moolenaar return;
3677071d4279SBram Moolenaar
36781966c248SBram Moolenaar if (ends_excmd2(eap->cmd, arg))
3679071d4279SBram Moolenaar {
3680071d4279SBram Moolenaar /*
3681071d4279SBram Moolenaar * No argument: Clear all syntax items.
3682071d4279SBram Moolenaar */
3683071d4279SBram Moolenaar if (syncing)
3684071d4279SBram Moolenaar syntax_sync_clear();
3685071d4279SBram Moolenaar else
3686071d4279SBram Moolenaar {
3687860cae1cSBram Moolenaar syntax_clear(curwin->w_s);
3688860cae1cSBram Moolenaar if (curwin->w_s == &curwin->w_buffer->b_s)
36892ce06f6eSBram Moolenaar do_unlet((char_u *)"b:current_syntax", TRUE);
3690860cae1cSBram Moolenaar do_unlet((char_u *)"w:current_syntax", TRUE);
3691071d4279SBram Moolenaar }
3692071d4279SBram Moolenaar }
3693071d4279SBram Moolenaar else
3694071d4279SBram Moolenaar {
3695071d4279SBram Moolenaar /*
3696071d4279SBram Moolenaar * Clear the group IDs that are in the argument.
3697071d4279SBram Moolenaar */
36981966c248SBram Moolenaar while (!ends_excmd2(eap->cmd, arg))
3699071d4279SBram Moolenaar {
3700071d4279SBram Moolenaar arg_end = skiptowhite(arg);
3701071d4279SBram Moolenaar if (*arg == '@')
3702071d4279SBram Moolenaar {
3703071d4279SBram Moolenaar id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3704071d4279SBram Moolenaar if (id == 0)
3705071d4279SBram Moolenaar {
3706f9e3e09fSBram Moolenaar semsg(_("E391: No such syntax cluster: %s"), arg);
3707071d4279SBram Moolenaar break;
3708071d4279SBram Moolenaar }
3709071d4279SBram Moolenaar else
3710071d4279SBram Moolenaar {
3711071d4279SBram Moolenaar /*
3712071d4279SBram Moolenaar * We can't physically delete a cluster without changing
3713071d4279SBram Moolenaar * the IDs of other clusters, so we do the next best thing
3714071d4279SBram Moolenaar * and make it empty.
3715071d4279SBram Moolenaar */
3716071d4279SBram Moolenaar short scl_id = id - SYNID_CLUSTER;
3717071d4279SBram Moolenaar
3718d23a8236SBram Moolenaar VIM_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3719071d4279SBram Moolenaar }
3720071d4279SBram Moolenaar }
3721071d4279SBram Moolenaar else
3722071d4279SBram Moolenaar {
3723071d4279SBram Moolenaar id = syn_namen2id(arg, (int)(arg_end - arg));
3724071d4279SBram Moolenaar if (id == 0)
3725071d4279SBram Moolenaar {
3726e29a27f6SBram Moolenaar semsg(_(e_no_such_highlight_group_name_str), arg);
3727071d4279SBram Moolenaar break;
3728071d4279SBram Moolenaar }
3729071d4279SBram Moolenaar else
3730071d4279SBram Moolenaar syn_clear_one(id, syncing);
3731071d4279SBram Moolenaar }
3732071d4279SBram Moolenaar arg = skipwhite(arg_end);
3733071d4279SBram Moolenaar }
3734071d4279SBram Moolenaar }
37351c8f93ffSBram Moolenaar redraw_curbuf_later(SOME_VALID);
37360d6f5d97SBram Moolenaar syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
3737071d4279SBram Moolenaar }
3738071d4279SBram Moolenaar
3739071d4279SBram Moolenaar /*
3740071d4279SBram Moolenaar * Clear one syntax group for the current buffer.
3741071d4279SBram Moolenaar */
3742071d4279SBram Moolenaar static void
syn_clear_one(int id,int syncing)3743764b23c8SBram Moolenaar syn_clear_one(int id, int syncing)
3744071d4279SBram Moolenaar {
3745071d4279SBram Moolenaar synpat_T *spp;
3746071d4279SBram Moolenaar int idx;
3747071d4279SBram Moolenaar
37480d6f5d97SBram Moolenaar // Clear keywords only when not ":syn sync clear group-name"
3749071d4279SBram Moolenaar if (!syncing)
3750071d4279SBram Moolenaar {
3751860cae1cSBram Moolenaar (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3752860cae1cSBram Moolenaar (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
3753071d4279SBram Moolenaar }
3754071d4279SBram Moolenaar
37550d6f5d97SBram Moolenaar // clear the patterns for "id"
3756860cae1cSBram Moolenaar for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
3757071d4279SBram Moolenaar {
3758860cae1cSBram Moolenaar spp = &(SYN_ITEMS(curwin->w_s)[idx]);
3759071d4279SBram Moolenaar if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3760071d4279SBram Moolenaar continue;
3761860cae1cSBram Moolenaar syn_remove_pattern(curwin->w_s, idx);
3762071d4279SBram Moolenaar }
3763071d4279SBram Moolenaar }
3764071d4279SBram Moolenaar
3765071d4279SBram Moolenaar /*
3766071d4279SBram Moolenaar * Handle ":syntax on" command.
3767071d4279SBram Moolenaar */
3768071d4279SBram Moolenaar static void
syn_cmd_on(exarg_T * eap,int syncing UNUSED)3769764b23c8SBram Moolenaar syn_cmd_on(exarg_T *eap, int syncing UNUSED)
3770071d4279SBram Moolenaar {
3771071d4279SBram Moolenaar syn_cmd_onoff(eap, "syntax");
3772071d4279SBram Moolenaar }
3773071d4279SBram Moolenaar
3774071d4279SBram Moolenaar /*
3775071d4279SBram Moolenaar * Handle ":syntax enable" command.
3776071d4279SBram Moolenaar */
3777071d4279SBram Moolenaar static void
syn_cmd_enable(exarg_T * eap,int syncing UNUSED)3778764b23c8SBram Moolenaar syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
3779071d4279SBram Moolenaar {
3780*c8a9fe54SBram Moolenaar set_internal_string_var((char_u *)"g:syntax_cmd", (char_u *)"enable");
3781071d4279SBram Moolenaar syn_cmd_onoff(eap, "syntax");
37822ce06f6eSBram Moolenaar do_unlet((char_u *)"g:syntax_cmd", TRUE);
3783071d4279SBram Moolenaar }
3784071d4279SBram Moolenaar
3785071d4279SBram Moolenaar /*
3786071d4279SBram Moolenaar * Handle ":syntax reset" command.
37878bc189e8SBram Moolenaar * It actually resets highlighting, not syntax.
3788071d4279SBram Moolenaar */
3789071d4279SBram Moolenaar static void
syn_cmd_reset(exarg_T * eap,int syncing UNUSED)3790764b23c8SBram Moolenaar syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
3791071d4279SBram Moolenaar {
379263b91736SBram Moolenaar set_nextcmd(eap, eap->arg);
3793071d4279SBram Moolenaar if (!eap->skip)
3794071d4279SBram Moolenaar {
3795*c8a9fe54SBram Moolenaar set_internal_string_var((char_u *)"g:syntax_cmd", (char_u *)"reset");
3796071d4279SBram Moolenaar do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
37972ce06f6eSBram Moolenaar do_unlet((char_u *)"g:syntax_cmd", TRUE);
3798071d4279SBram Moolenaar }
3799071d4279SBram Moolenaar }
3800071d4279SBram Moolenaar
3801071d4279SBram Moolenaar /*
3802071d4279SBram Moolenaar * Handle ":syntax manual" command.
3803071d4279SBram Moolenaar */
3804071d4279SBram Moolenaar static void
syn_cmd_manual(exarg_T * eap,int syncing UNUSED)3805764b23c8SBram Moolenaar syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
3806071d4279SBram Moolenaar {
3807071d4279SBram Moolenaar syn_cmd_onoff(eap, "manual");
3808071d4279SBram Moolenaar }
3809071d4279SBram Moolenaar
3810071d4279SBram Moolenaar /*
3811071d4279SBram Moolenaar * Handle ":syntax off" command.
3812071d4279SBram Moolenaar */
3813071d4279SBram Moolenaar static void
syn_cmd_off(exarg_T * eap,int syncing UNUSED)3814764b23c8SBram Moolenaar syn_cmd_off(exarg_T *eap, int syncing UNUSED)
3815071d4279SBram Moolenaar {
3816071d4279SBram Moolenaar syn_cmd_onoff(eap, "nosyntax");
3817071d4279SBram Moolenaar }
3818071d4279SBram Moolenaar
3819071d4279SBram Moolenaar static void
syn_cmd_onoff(exarg_T * eap,char * name)3820764b23c8SBram Moolenaar syn_cmd_onoff(exarg_T *eap, char *name)
3821071d4279SBram Moolenaar {
3822071d4279SBram Moolenaar char_u buf[100];
3823071d4279SBram Moolenaar
382463b91736SBram Moolenaar set_nextcmd(eap, eap->arg);
3825071d4279SBram Moolenaar if (!eap->skip)
3826071d4279SBram Moolenaar {
3827071d4279SBram Moolenaar STRCPY(buf, "so ");
3828555b280fSBram Moolenaar vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
3829071d4279SBram Moolenaar do_cmdline_cmd(buf);
3830071d4279SBram Moolenaar }
3831071d4279SBram Moolenaar }
3832071d4279SBram Moolenaar
3833071d4279SBram Moolenaar /*
3834071d4279SBram Moolenaar * Handle ":syntax [list]" command: list current syntax words.
3835071d4279SBram Moolenaar */
3836071d4279SBram Moolenaar static void
syn_cmd_list(exarg_T * eap,int syncing)3837764b23c8SBram Moolenaar syn_cmd_list(
3838764b23c8SBram Moolenaar exarg_T *eap,
38390d6f5d97SBram Moolenaar int syncing) // when TRUE: list syncing items
3840071d4279SBram Moolenaar {
3841071d4279SBram Moolenaar char_u *arg = eap->arg;
3842071d4279SBram Moolenaar int id;
3843071d4279SBram Moolenaar char_u *arg_end;
3844071d4279SBram Moolenaar
3845071d4279SBram Moolenaar eap->nextcmd = find_nextcmd(arg);
3846071d4279SBram Moolenaar if (eap->skip)
3847071d4279SBram Moolenaar return;
3848071d4279SBram Moolenaar
3849860cae1cSBram Moolenaar if (!syntax_present(curwin))
3850071d4279SBram Moolenaar {
385132526b3cSBram Moolenaar msg(_(msg_no_items));
3852071d4279SBram Moolenaar return;
3853071d4279SBram Moolenaar }
3854071d4279SBram Moolenaar
3855071d4279SBram Moolenaar if (syncing)
3856071d4279SBram Moolenaar {
3857860cae1cSBram Moolenaar if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
3858071d4279SBram Moolenaar {
385932526b3cSBram Moolenaar msg_puts(_("syncing on C-style comments"));
3860071d4279SBram Moolenaar syn_lines_msg();
3861071d4279SBram Moolenaar syn_match_msg();
3862071d4279SBram Moolenaar return;
3863071d4279SBram Moolenaar }
3864860cae1cSBram Moolenaar else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
3865071d4279SBram Moolenaar {
3866860cae1cSBram Moolenaar if (curwin->w_s->b_syn_sync_minlines == 0)
386732526b3cSBram Moolenaar msg_puts(_("no syncing"));
3868071d4279SBram Moolenaar else
3869071d4279SBram Moolenaar {
38709950280dSBram Moolenaar if (curwin->w_s->b_syn_sync_minlines == MAXLNUM)
38719950280dSBram Moolenaar msg_puts(_("syncing starts at the first line"));
38729950280dSBram Moolenaar else
38739950280dSBram Moolenaar {
387432526b3cSBram Moolenaar msg_puts(_("syncing starts "));
3875860cae1cSBram Moolenaar msg_outnum(curwin->w_s->b_syn_sync_minlines);
387632526b3cSBram Moolenaar msg_puts(_(" lines before top line"));
38779950280dSBram Moolenaar }
3878071d4279SBram Moolenaar syn_match_msg();
3879071d4279SBram Moolenaar }
3880071d4279SBram Moolenaar return;
3881071d4279SBram Moolenaar }
388232526b3cSBram Moolenaar msg_puts_title(_("\n--- Syntax sync items ---"));
3883860cae1cSBram Moolenaar if (curwin->w_s->b_syn_sync_minlines > 0
3884860cae1cSBram Moolenaar || curwin->w_s->b_syn_sync_maxlines > 0
3885860cae1cSBram Moolenaar || curwin->w_s->b_syn_sync_linebreaks > 0)
3886071d4279SBram Moolenaar {
388732526b3cSBram Moolenaar msg_puts(_("\nsyncing on items"));
3888071d4279SBram Moolenaar syn_lines_msg();
3889071d4279SBram Moolenaar syn_match_msg();
3890071d4279SBram Moolenaar }
3891071d4279SBram Moolenaar }
3892071d4279SBram Moolenaar else
389332526b3cSBram Moolenaar msg_puts_title(_("\n--- Syntax items ---"));
38941966c248SBram Moolenaar if (ends_excmd2(eap->cmd, arg))
3895071d4279SBram Moolenaar {
3896071d4279SBram Moolenaar /*
3897071d4279SBram Moolenaar * No argument: List all group IDs and all syntax clusters.
3898071d4279SBram Moolenaar */
38992ac6e82aSBram Moolenaar for (id = 1; id <= highlight_num_groups() && !got_int; ++id)
3900071d4279SBram Moolenaar syn_list_one(id, syncing, FALSE);
3901860cae1cSBram Moolenaar for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
3902071d4279SBram Moolenaar syn_list_cluster(id);
3903071d4279SBram Moolenaar }
3904071d4279SBram Moolenaar else
3905071d4279SBram Moolenaar {
3906071d4279SBram Moolenaar /*
3907071d4279SBram Moolenaar * List the group IDs and syntax clusters that are in the argument.
3908071d4279SBram Moolenaar */
39091966c248SBram Moolenaar while (!ends_excmd2(eap->cmd, arg) && !got_int)
3910071d4279SBram Moolenaar {
3911071d4279SBram Moolenaar arg_end = skiptowhite(arg);
3912071d4279SBram Moolenaar if (*arg == '@')
3913071d4279SBram Moolenaar {
3914071d4279SBram Moolenaar id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3915071d4279SBram Moolenaar if (id == 0)
3916f9e3e09fSBram Moolenaar semsg(_("E392: No such syntax cluster: %s"), arg);
3917071d4279SBram Moolenaar else
3918071d4279SBram Moolenaar syn_list_cluster(id - SYNID_CLUSTER);
3919071d4279SBram Moolenaar }
3920071d4279SBram Moolenaar else
3921071d4279SBram Moolenaar {
3922071d4279SBram Moolenaar id = syn_namen2id(arg, (int)(arg_end - arg));
3923071d4279SBram Moolenaar if (id == 0)
3924e29a27f6SBram Moolenaar semsg(_(e_no_such_highlight_group_name_str), arg);
3925071d4279SBram Moolenaar else
3926071d4279SBram Moolenaar syn_list_one(id, syncing, TRUE);
3927071d4279SBram Moolenaar }
3928071d4279SBram Moolenaar arg = skipwhite(arg_end);
3929071d4279SBram Moolenaar }
3930071d4279SBram Moolenaar }
393163b91736SBram Moolenaar set_nextcmd(eap, arg);
3932071d4279SBram Moolenaar }
3933071d4279SBram Moolenaar
3934071d4279SBram Moolenaar static void
syn_lines_msg(void)3935764b23c8SBram Moolenaar syn_lines_msg(void)
3936071d4279SBram Moolenaar {
3937860cae1cSBram Moolenaar if (curwin->w_s->b_syn_sync_maxlines > 0
3938860cae1cSBram Moolenaar || curwin->w_s->b_syn_sync_minlines > 0)
3939071d4279SBram Moolenaar {
394032526b3cSBram Moolenaar msg_puts("; ");
39419950280dSBram Moolenaar if (curwin->w_s->b_syn_sync_minlines == MAXLNUM)
39429950280dSBram Moolenaar msg_puts(_("from the first line"));
39439950280dSBram Moolenaar else
39449950280dSBram Moolenaar {
3945860cae1cSBram Moolenaar if (curwin->w_s->b_syn_sync_minlines > 0)
3946071d4279SBram Moolenaar {
394732526b3cSBram Moolenaar msg_puts(_("minimal "));
3948860cae1cSBram Moolenaar msg_outnum(curwin->w_s->b_syn_sync_minlines);
3949860cae1cSBram Moolenaar if (curwin->w_s->b_syn_sync_maxlines)
395032526b3cSBram Moolenaar msg_puts(", ");
3951071d4279SBram Moolenaar }
3952860cae1cSBram Moolenaar if (curwin->w_s->b_syn_sync_maxlines > 0)
3953071d4279SBram Moolenaar {
395432526b3cSBram Moolenaar msg_puts(_("maximal "));
3955860cae1cSBram Moolenaar msg_outnum(curwin->w_s->b_syn_sync_maxlines);
3956071d4279SBram Moolenaar }
395732526b3cSBram Moolenaar msg_puts(_(" lines before top line"));
3958071d4279SBram Moolenaar }
3959071d4279SBram Moolenaar }
39609950280dSBram Moolenaar }
3961071d4279SBram Moolenaar
3962071d4279SBram Moolenaar static void
syn_match_msg(void)3963764b23c8SBram Moolenaar syn_match_msg(void)
3964071d4279SBram Moolenaar {
3965860cae1cSBram Moolenaar if (curwin->w_s->b_syn_sync_linebreaks > 0)
3966071d4279SBram Moolenaar {
396732526b3cSBram Moolenaar msg_puts(_("; match "));
3968860cae1cSBram Moolenaar msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
396932526b3cSBram Moolenaar msg_puts(_(" line breaks"));
3970071d4279SBram Moolenaar }
3971071d4279SBram Moolenaar }
3972071d4279SBram Moolenaar
3973071d4279SBram Moolenaar static int last_matchgroup;
3974071d4279SBram Moolenaar
3975071d4279SBram Moolenaar struct name_list
3976071d4279SBram Moolenaar {
3977071d4279SBram Moolenaar int flag;
3978071d4279SBram Moolenaar char *name;
3979071d4279SBram Moolenaar };
3980071d4279SBram Moolenaar
3981baaa7e9eSBram Moolenaar static void syn_list_flags(struct name_list *nl, int flags, int attr);
3982071d4279SBram Moolenaar
3983071d4279SBram Moolenaar /*
3984071d4279SBram Moolenaar * List one syntax item, for ":syntax" or "syntax list syntax_name".
3985071d4279SBram Moolenaar */
3986071d4279SBram Moolenaar static void
syn_list_one(int id,int syncing,int link_only)3987764b23c8SBram Moolenaar syn_list_one(
3988764b23c8SBram Moolenaar int id,
39890d6f5d97SBram Moolenaar int syncing, // when TRUE: list syncing items
39900d6f5d97SBram Moolenaar int link_only) // when TRUE; list link-only too
3991071d4279SBram Moolenaar {
3992071d4279SBram Moolenaar int attr;
3993071d4279SBram Moolenaar int idx;
3994071d4279SBram Moolenaar int did_header = FALSE;
3995071d4279SBram Moolenaar synpat_T *spp;
3996071d4279SBram Moolenaar static struct name_list namelist1[] =
3997071d4279SBram Moolenaar {
3998071d4279SBram Moolenaar {HL_DISPLAY, "display"},
3999071d4279SBram Moolenaar {HL_CONTAINED, "contained"},
4000071d4279SBram Moolenaar {HL_ONELINE, "oneline"},
4001071d4279SBram Moolenaar {HL_KEEPEND, "keepend"},
4002071d4279SBram Moolenaar {HL_EXTEND, "extend"},
4003071d4279SBram Moolenaar {HL_EXCLUDENL, "excludenl"},
4004071d4279SBram Moolenaar {HL_TRANSP, "transparent"},
4005071d4279SBram Moolenaar {HL_FOLD, "fold"},
4006860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
4007860cae1cSBram Moolenaar {HL_CONCEAL, "conceal"},
4008860cae1cSBram Moolenaar {HL_CONCEALENDS, "concealends"},
4009860cae1cSBram Moolenaar #endif
4010071d4279SBram Moolenaar {0, NULL}
4011071d4279SBram Moolenaar };
4012071d4279SBram Moolenaar static struct name_list namelist2[] =
4013071d4279SBram Moolenaar {
4014071d4279SBram Moolenaar {HL_SKIPWHITE, "skipwhite"},
4015071d4279SBram Moolenaar {HL_SKIPNL, "skipnl"},
4016071d4279SBram Moolenaar {HL_SKIPEMPTY, "skipempty"},
4017071d4279SBram Moolenaar {0, NULL}
4018071d4279SBram Moolenaar };
4019071d4279SBram Moolenaar
40200d6f5d97SBram Moolenaar attr = HL_ATTR(HLF_D); // highlight like directories
4021071d4279SBram Moolenaar
40220d6f5d97SBram Moolenaar // list the keywords for "id"
4023071d4279SBram Moolenaar if (!syncing)
4024071d4279SBram Moolenaar {
4025860cae1cSBram Moolenaar did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4026860cae1cSBram Moolenaar did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
4027071d4279SBram Moolenaar did_header, attr);
4028071d4279SBram Moolenaar }
4029071d4279SBram Moolenaar
40300d6f5d97SBram Moolenaar // list the patterns for "id"
4031860cae1cSBram Moolenaar for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
4032071d4279SBram Moolenaar {
4033860cae1cSBram Moolenaar spp = &(SYN_ITEMS(curwin->w_s)[idx]);
4034071d4279SBram Moolenaar if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4035071d4279SBram Moolenaar continue;
4036071d4279SBram Moolenaar
4037071d4279SBram Moolenaar (void)syn_list_header(did_header, 999, id);
4038071d4279SBram Moolenaar did_header = TRUE;
4039071d4279SBram Moolenaar last_matchgroup = 0;
4040071d4279SBram Moolenaar if (spp->sp_type == SPTYPE_MATCH)
4041071d4279SBram Moolenaar {
4042071d4279SBram Moolenaar put_pattern("match", ' ', spp, attr);
4043071d4279SBram Moolenaar msg_putchar(' ');
4044071d4279SBram Moolenaar }
4045071d4279SBram Moolenaar else if (spp->sp_type == SPTYPE_START)
4046071d4279SBram Moolenaar {
4047860cae1cSBram Moolenaar while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4048860cae1cSBram Moolenaar put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4049860cae1cSBram Moolenaar if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4050860cae1cSBram Moolenaar put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4051860cae1cSBram Moolenaar while (idx < curwin->w_s->b_syn_patterns.ga_len
4052860cae1cSBram Moolenaar && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4053860cae1cSBram Moolenaar put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4054071d4279SBram Moolenaar --idx;
4055071d4279SBram Moolenaar msg_putchar(' ');
4056071d4279SBram Moolenaar }
4057071d4279SBram Moolenaar syn_list_flags(namelist1, spp->sp_flags, attr);
4058071d4279SBram Moolenaar
4059071d4279SBram Moolenaar if (spp->sp_cont_list != NULL)
4060071d4279SBram Moolenaar put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4061071d4279SBram Moolenaar
4062071d4279SBram Moolenaar if (spp->sp_syn.cont_in_list != NULL)
4063071d4279SBram Moolenaar put_id_list((char_u *)"containedin",
4064071d4279SBram Moolenaar spp->sp_syn.cont_in_list, attr);
4065071d4279SBram Moolenaar
4066071d4279SBram Moolenaar if (spp->sp_next_list != NULL)
4067071d4279SBram Moolenaar {
4068071d4279SBram Moolenaar put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4069071d4279SBram Moolenaar syn_list_flags(namelist2, spp->sp_flags, attr);
4070071d4279SBram Moolenaar }
4071071d4279SBram Moolenaar if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4072071d4279SBram Moolenaar {
4073071d4279SBram Moolenaar if (spp->sp_flags & HL_SYNC_HERE)
407432526b3cSBram Moolenaar msg_puts_attr("grouphere", attr);
4075071d4279SBram Moolenaar else
407632526b3cSBram Moolenaar msg_puts_attr("groupthere", attr);
4077071d4279SBram Moolenaar msg_putchar(' ');
4078071d4279SBram Moolenaar if (spp->sp_sync_idx >= 0)
40792ac6e82aSBram Moolenaar msg_outtrans(highlight_group_name(SYN_ITEMS(curwin->w_s)
40802ac6e82aSBram Moolenaar [spp->sp_sync_idx].sp_syn.id - 1));
4081071d4279SBram Moolenaar else
408232526b3cSBram Moolenaar msg_puts("NONE");
4083071d4279SBram Moolenaar msg_putchar(' ');
4084071d4279SBram Moolenaar }
4085071d4279SBram Moolenaar }
4086071d4279SBram Moolenaar
40870d6f5d97SBram Moolenaar // list the link, if there is one
40882ac6e82aSBram Moolenaar if (highlight_link_id(id - 1) && (did_header || link_only) && !got_int)
4089071d4279SBram Moolenaar {
4090071d4279SBram Moolenaar (void)syn_list_header(did_header, 999, id);
409132526b3cSBram Moolenaar msg_puts_attr("links to", attr);
4092071d4279SBram Moolenaar msg_putchar(' ');
40932ac6e82aSBram Moolenaar msg_outtrans(highlight_group_name(highlight_link_id(id - 1) - 1));
4094071d4279SBram Moolenaar }
4095071d4279SBram Moolenaar }
4096071d4279SBram Moolenaar
4097071d4279SBram Moolenaar static void
syn_list_flags(struct name_list * nlist,int flags,int attr)4098764b23c8SBram Moolenaar syn_list_flags(struct name_list *nlist, int flags, int attr)
4099071d4279SBram Moolenaar {
4100071d4279SBram Moolenaar int i;
4101071d4279SBram Moolenaar
410270b2a56dSBram Moolenaar for (i = 0; nlist[i].flag != 0; ++i)
410370b2a56dSBram Moolenaar if (flags & nlist[i].flag)
4104071d4279SBram Moolenaar {
410532526b3cSBram Moolenaar msg_puts_attr(nlist[i].name, attr);
4106071d4279SBram Moolenaar msg_putchar(' ');
4107071d4279SBram Moolenaar }
4108071d4279SBram Moolenaar }
4109071d4279SBram Moolenaar
4110071d4279SBram Moolenaar /*
4111071d4279SBram Moolenaar * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4112071d4279SBram Moolenaar */
4113071d4279SBram Moolenaar static void
syn_list_cluster(int id)4114764b23c8SBram Moolenaar syn_list_cluster(int id)
4115071d4279SBram Moolenaar {
4116071d4279SBram Moolenaar int endcol = 15;
4117071d4279SBram Moolenaar
41180d6f5d97SBram Moolenaar // slight hack: roughly duplicate the guts of syn_list_header()
4119071d4279SBram Moolenaar msg_putchar('\n');
4120860cae1cSBram Moolenaar msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
4121071d4279SBram Moolenaar
41220d6f5d97SBram Moolenaar if (msg_col >= endcol) // output at least one space
4123071d4279SBram Moolenaar endcol = msg_col + 1;
41240d6f5d97SBram Moolenaar if (Columns <= endcol) // avoid hang for tiny window
4125071d4279SBram Moolenaar endcol = Columns - 1;
4126071d4279SBram Moolenaar
4127071d4279SBram Moolenaar msg_advance(endcol);
4128860cae1cSBram Moolenaar if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
4129071d4279SBram Moolenaar {
4130860cae1cSBram Moolenaar put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
41318820b486SBram Moolenaar HL_ATTR(HLF_D));
4132071d4279SBram Moolenaar }
4133071d4279SBram Moolenaar else
4134071d4279SBram Moolenaar {
413532526b3cSBram Moolenaar msg_puts_attr("cluster", HL_ATTR(HLF_D));
413632526b3cSBram Moolenaar msg_puts("=NONE");
4137071d4279SBram Moolenaar }
4138071d4279SBram Moolenaar }
4139071d4279SBram Moolenaar
4140071d4279SBram Moolenaar static void
put_id_list(char_u * name,short * list,int attr)4141764b23c8SBram Moolenaar put_id_list(char_u *name, short *list, int attr)
4142071d4279SBram Moolenaar {
4143071d4279SBram Moolenaar short *p;
4144071d4279SBram Moolenaar
414532526b3cSBram Moolenaar msg_puts_attr((char *)name, attr);
4146071d4279SBram Moolenaar msg_putchar('=');
4147071d4279SBram Moolenaar for (p = list; *p; ++p)
4148071d4279SBram Moolenaar {
4149071d4279SBram Moolenaar if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4150071d4279SBram Moolenaar {
4151071d4279SBram Moolenaar if (p[1])
415232526b3cSBram Moolenaar msg_puts("ALLBUT");
4153071d4279SBram Moolenaar else
415432526b3cSBram Moolenaar msg_puts("ALL");
4155071d4279SBram Moolenaar }
4156071d4279SBram Moolenaar else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4157071d4279SBram Moolenaar {
415832526b3cSBram Moolenaar msg_puts("TOP");
4159071d4279SBram Moolenaar }
4160071d4279SBram Moolenaar else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4161071d4279SBram Moolenaar {
416232526b3cSBram Moolenaar msg_puts("CONTAINED");
4163071d4279SBram Moolenaar }
4164071d4279SBram Moolenaar else if (*p >= SYNID_CLUSTER)
4165071d4279SBram Moolenaar {
4166071d4279SBram Moolenaar short scl_id = *p - SYNID_CLUSTER;
4167071d4279SBram Moolenaar
4168071d4279SBram Moolenaar msg_putchar('@');
4169860cae1cSBram Moolenaar msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
4170071d4279SBram Moolenaar }
4171071d4279SBram Moolenaar else
41722ac6e82aSBram Moolenaar msg_outtrans(highlight_group_name(*p - 1));
4173071d4279SBram Moolenaar if (p[1])
4174071d4279SBram Moolenaar msg_putchar(',');
4175071d4279SBram Moolenaar }
4176071d4279SBram Moolenaar msg_putchar(' ');
4177071d4279SBram Moolenaar }
4178071d4279SBram Moolenaar
4179071d4279SBram Moolenaar static void
put_pattern(char * s,int c,synpat_T * spp,int attr)4180764b23c8SBram Moolenaar put_pattern(
4181764b23c8SBram Moolenaar char *s,
4182764b23c8SBram Moolenaar int c,
4183764b23c8SBram Moolenaar synpat_T *spp,
4184764b23c8SBram Moolenaar int attr)
4185071d4279SBram Moolenaar {
4186071d4279SBram Moolenaar long n;
4187071d4279SBram Moolenaar int mask;
4188071d4279SBram Moolenaar int first;
4189071d4279SBram Moolenaar static char *sepchars = "/+=-#@\"|'^&";
4190071d4279SBram Moolenaar int i;
4191071d4279SBram Moolenaar
41920d6f5d97SBram Moolenaar // May have to write "matchgroup=group"
4193071d4279SBram Moolenaar if (last_matchgroup != spp->sp_syn_match_id)
4194071d4279SBram Moolenaar {
4195071d4279SBram Moolenaar last_matchgroup = spp->sp_syn_match_id;
419632526b3cSBram Moolenaar msg_puts_attr("matchgroup", attr);
4197071d4279SBram Moolenaar msg_putchar('=');
4198071d4279SBram Moolenaar if (last_matchgroup == 0)
4199071d4279SBram Moolenaar msg_outtrans((char_u *)"NONE");
4200071d4279SBram Moolenaar else
42012ac6e82aSBram Moolenaar msg_outtrans(highlight_group_name(last_matchgroup - 1));
4202071d4279SBram Moolenaar msg_putchar(' ');
4203071d4279SBram Moolenaar }
4204071d4279SBram Moolenaar
42050d6f5d97SBram Moolenaar // output the name of the pattern and an '=' or ' '
420632526b3cSBram Moolenaar msg_puts_attr(s, attr);
4207071d4279SBram Moolenaar msg_putchar(c);
4208071d4279SBram Moolenaar
42090d6f5d97SBram Moolenaar // output the pattern, in between a char that is not in the pattern
4210071d4279SBram Moolenaar for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4211071d4279SBram Moolenaar if (sepchars[++i] == NUL)
4212071d4279SBram Moolenaar {
42130d6f5d97SBram Moolenaar i = 0; // no good char found, just use the first one
4214071d4279SBram Moolenaar break;
4215071d4279SBram Moolenaar }
4216071d4279SBram Moolenaar msg_putchar(sepchars[i]);
4217071d4279SBram Moolenaar msg_outtrans(spp->sp_pattern);
4218071d4279SBram Moolenaar msg_putchar(sepchars[i]);
4219071d4279SBram Moolenaar
42200d6f5d97SBram Moolenaar // output any pattern options
4221071d4279SBram Moolenaar first = TRUE;
4222071d4279SBram Moolenaar for (i = 0; i < SPO_COUNT; ++i)
4223071d4279SBram Moolenaar {
4224071d4279SBram Moolenaar mask = (1 << i);
4225071d4279SBram Moolenaar if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4226071d4279SBram Moolenaar {
4227071d4279SBram Moolenaar if (!first)
42280d6f5d97SBram Moolenaar msg_putchar(','); // separate with commas
422932526b3cSBram Moolenaar msg_puts(spo_name_tab[i]);
4230071d4279SBram Moolenaar n = spp->sp_offsets[i];
4231071d4279SBram Moolenaar if (i != SPO_LC_OFF)
4232071d4279SBram Moolenaar {
4233071d4279SBram Moolenaar if (spp->sp_off_flags & mask)
4234071d4279SBram Moolenaar msg_putchar('s');
4235071d4279SBram Moolenaar else
4236071d4279SBram Moolenaar msg_putchar('e');
4237071d4279SBram Moolenaar if (n > 0)
4238071d4279SBram Moolenaar msg_putchar('+');
4239071d4279SBram Moolenaar }
4240071d4279SBram Moolenaar if (n || i == SPO_LC_OFF)
4241071d4279SBram Moolenaar msg_outnum(n);
4242071d4279SBram Moolenaar first = FALSE;
4243071d4279SBram Moolenaar }
4244071d4279SBram Moolenaar }
4245071d4279SBram Moolenaar msg_putchar(' ');
4246071d4279SBram Moolenaar }
4247071d4279SBram Moolenaar
4248071d4279SBram Moolenaar /*
4249071d4279SBram Moolenaar * List or clear the keywords for one syntax group.
4250071d4279SBram Moolenaar * Return TRUE if the header has been printed.
4251071d4279SBram Moolenaar */
4252071d4279SBram Moolenaar static int
syn_list_keywords(int id,hashtab_T * ht,int did_header,int attr)4253764b23c8SBram Moolenaar syn_list_keywords(
4254764b23c8SBram Moolenaar int id,
4255764b23c8SBram Moolenaar hashtab_T *ht,
42560d6f5d97SBram Moolenaar int did_header, // header has already been printed
4257764b23c8SBram Moolenaar int attr)
4258071d4279SBram Moolenaar {
4259071d4279SBram Moolenaar int outlen;
4260dad6b69cSBram Moolenaar hashitem_T *hi;
4261dad6b69cSBram Moolenaar keyentry_T *kp;
4262dad6b69cSBram Moolenaar int todo;
4263071d4279SBram Moolenaar int prev_contained = 0;
4264071d4279SBram Moolenaar short *prev_next_list = NULL;
4265071d4279SBram Moolenaar short *prev_cont_in_list = NULL;
4266071d4279SBram Moolenaar int prev_skipnl = 0;
4267071d4279SBram Moolenaar int prev_skipwhite = 0;
4268071d4279SBram Moolenaar int prev_skipempty = 0;
4269071d4279SBram Moolenaar
4270071d4279SBram Moolenaar /*
4271071d4279SBram Moolenaar * Unfortunately, this list of keywords is not sorted on alphabet but on
4272071d4279SBram Moolenaar * hash value...
4273071d4279SBram Moolenaar */
4274a93fa7eeSBram Moolenaar todo = (int)ht->ht_used;
4275dad6b69cSBram Moolenaar for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
4276071d4279SBram Moolenaar {
4277dad6b69cSBram Moolenaar if (!HASHITEM_EMPTY(hi))
4278071d4279SBram Moolenaar {
4279dad6b69cSBram Moolenaar --todo;
4280dad6b69cSBram Moolenaar for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
4281071d4279SBram Moolenaar {
4282dad6b69cSBram Moolenaar if (kp->k_syn.id == id)
4283dad6b69cSBram Moolenaar {
4284dad6b69cSBram Moolenaar if (prev_contained != (kp->flags & HL_CONTAINED)
4285dad6b69cSBram Moolenaar || prev_skipnl != (kp->flags & HL_SKIPNL)
4286dad6b69cSBram Moolenaar || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4287dad6b69cSBram Moolenaar || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4288dad6b69cSBram Moolenaar || prev_cont_in_list != kp->k_syn.cont_in_list
4289dad6b69cSBram Moolenaar || prev_next_list != kp->next_list)
4290071d4279SBram Moolenaar outlen = 9999;
4291071d4279SBram Moolenaar else
4292dad6b69cSBram Moolenaar outlen = (int)STRLEN(kp->keyword);
42930d6f5d97SBram Moolenaar // output "contained" and "nextgroup" on each line
4294071d4279SBram Moolenaar if (syn_list_header(did_header, outlen, id))
4295071d4279SBram Moolenaar {
4296071d4279SBram Moolenaar prev_contained = 0;
4297071d4279SBram Moolenaar prev_next_list = NULL;
4298071d4279SBram Moolenaar prev_cont_in_list = NULL;
4299071d4279SBram Moolenaar prev_skipnl = 0;
4300071d4279SBram Moolenaar prev_skipwhite = 0;
4301071d4279SBram Moolenaar prev_skipempty = 0;
4302071d4279SBram Moolenaar }
4303071d4279SBram Moolenaar did_header = TRUE;
4304dad6b69cSBram Moolenaar if (prev_contained != (kp->flags & HL_CONTAINED))
4305071d4279SBram Moolenaar {
430632526b3cSBram Moolenaar msg_puts_attr("contained", attr);
4307071d4279SBram Moolenaar msg_putchar(' ');
4308dad6b69cSBram Moolenaar prev_contained = (kp->flags & HL_CONTAINED);
4309071d4279SBram Moolenaar }
4310dad6b69cSBram Moolenaar if (kp->k_syn.cont_in_list != prev_cont_in_list)
4311071d4279SBram Moolenaar {
4312071d4279SBram Moolenaar put_id_list((char_u *)"containedin",
4313dad6b69cSBram Moolenaar kp->k_syn.cont_in_list, attr);
4314071d4279SBram Moolenaar msg_putchar(' ');
4315dad6b69cSBram Moolenaar prev_cont_in_list = kp->k_syn.cont_in_list;
4316071d4279SBram Moolenaar }
4317dad6b69cSBram Moolenaar if (kp->next_list != prev_next_list)
4318071d4279SBram Moolenaar {
4319dad6b69cSBram Moolenaar put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4320071d4279SBram Moolenaar msg_putchar(' ');
4321dad6b69cSBram Moolenaar prev_next_list = kp->next_list;
4322dad6b69cSBram Moolenaar if (kp->flags & HL_SKIPNL)
4323071d4279SBram Moolenaar {
432432526b3cSBram Moolenaar msg_puts_attr("skipnl", attr);
4325071d4279SBram Moolenaar msg_putchar(' ');
4326dad6b69cSBram Moolenaar prev_skipnl = (kp->flags & HL_SKIPNL);
4327071d4279SBram Moolenaar }
4328dad6b69cSBram Moolenaar if (kp->flags & HL_SKIPWHITE)
4329071d4279SBram Moolenaar {
433032526b3cSBram Moolenaar msg_puts_attr("skipwhite", attr);
4331071d4279SBram Moolenaar msg_putchar(' ');
4332dad6b69cSBram Moolenaar prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4333071d4279SBram Moolenaar }
4334dad6b69cSBram Moolenaar if (kp->flags & HL_SKIPEMPTY)
4335071d4279SBram Moolenaar {
433632526b3cSBram Moolenaar msg_puts_attr("skipempty", attr);
4337071d4279SBram Moolenaar msg_putchar(' ');
4338dad6b69cSBram Moolenaar prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4339071d4279SBram Moolenaar }
4340071d4279SBram Moolenaar }
4341dad6b69cSBram Moolenaar msg_outtrans(kp->keyword);
4342dad6b69cSBram Moolenaar }
4343071d4279SBram Moolenaar }
4344071d4279SBram Moolenaar }
4345071d4279SBram Moolenaar }
4346071d4279SBram Moolenaar
4347071d4279SBram Moolenaar return did_header;
4348071d4279SBram Moolenaar }
4349071d4279SBram Moolenaar
4350071d4279SBram Moolenaar static void
syn_clear_keyword(int id,hashtab_T * ht)4351764b23c8SBram Moolenaar syn_clear_keyword(int id, hashtab_T *ht)
4352071d4279SBram Moolenaar {
4353dad6b69cSBram Moolenaar hashitem_T *hi;
4354dad6b69cSBram Moolenaar keyentry_T *kp;
4355dad6b69cSBram Moolenaar keyentry_T *kp_prev;
4356dad6b69cSBram Moolenaar keyentry_T *kp_next;
4357dad6b69cSBram Moolenaar int todo;
4358071d4279SBram Moolenaar
4359dad6b69cSBram Moolenaar hash_lock(ht);
4360a93fa7eeSBram Moolenaar todo = (int)ht->ht_used;
4361dad6b69cSBram Moolenaar for (hi = ht->ht_array; todo > 0; ++hi)
4362071d4279SBram Moolenaar {
4363dad6b69cSBram Moolenaar if (!HASHITEM_EMPTY(hi))
4364071d4279SBram Moolenaar {
4365dad6b69cSBram Moolenaar --todo;
4366dad6b69cSBram Moolenaar kp_prev = NULL;
4367dad6b69cSBram Moolenaar for (kp = HI2KE(hi); kp != NULL; )
4368071d4279SBram Moolenaar {
4369dad6b69cSBram Moolenaar if (kp->k_syn.id == id)
4370dad6b69cSBram Moolenaar {
4371dad6b69cSBram Moolenaar kp_next = kp->ke_next;
4372dad6b69cSBram Moolenaar if (kp_prev == NULL)
4373dad6b69cSBram Moolenaar {
4374dad6b69cSBram Moolenaar if (kp_next == NULL)
4375dad6b69cSBram Moolenaar hash_remove(ht, hi);
4376071d4279SBram Moolenaar else
4377dad6b69cSBram Moolenaar hi->hi_key = KE2HIKEY(kp_next);
4378dad6b69cSBram Moolenaar }
4379dad6b69cSBram Moolenaar else
4380dad6b69cSBram Moolenaar kp_prev->ke_next = kp_next;
4381dad6b69cSBram Moolenaar vim_free(kp->next_list);
4382dad6b69cSBram Moolenaar vim_free(kp->k_syn.cont_in_list);
4383dad6b69cSBram Moolenaar vim_free(kp);
4384dad6b69cSBram Moolenaar kp = kp_next;
4385071d4279SBram Moolenaar }
4386071d4279SBram Moolenaar else
4387071d4279SBram Moolenaar {
4388dad6b69cSBram Moolenaar kp_prev = kp;
4389dad6b69cSBram Moolenaar kp = kp->ke_next;
4390071d4279SBram Moolenaar }
4391071d4279SBram Moolenaar }
4392071d4279SBram Moolenaar }
4393071d4279SBram Moolenaar }
4394dad6b69cSBram Moolenaar hash_unlock(ht);
4395dad6b69cSBram Moolenaar }
4396071d4279SBram Moolenaar
4397071d4279SBram Moolenaar /*
4398dad6b69cSBram Moolenaar * Clear a whole keyword table.
4399071d4279SBram Moolenaar */
4400071d4279SBram Moolenaar static void
clear_keywtab(hashtab_T * ht)4401764b23c8SBram Moolenaar clear_keywtab(hashtab_T *ht)
4402071d4279SBram Moolenaar {
4403dad6b69cSBram Moolenaar hashitem_T *hi;
4404dad6b69cSBram Moolenaar int todo;
4405dad6b69cSBram Moolenaar keyentry_T *kp;
4406dad6b69cSBram Moolenaar keyentry_T *kp_next;
4407071d4279SBram Moolenaar
4408a93fa7eeSBram Moolenaar todo = (int)ht->ht_used;
4409dad6b69cSBram Moolenaar for (hi = ht->ht_array; todo > 0; ++hi)
4410071d4279SBram Moolenaar {
4411dad6b69cSBram Moolenaar if (!HASHITEM_EMPTY(hi))
4412071d4279SBram Moolenaar {
4413dad6b69cSBram Moolenaar --todo;
4414dad6b69cSBram Moolenaar for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
4415dad6b69cSBram Moolenaar {
4416dad6b69cSBram Moolenaar kp_next = kp->ke_next;
4417dad6b69cSBram Moolenaar vim_free(kp->next_list);
4418dad6b69cSBram Moolenaar vim_free(kp->k_syn.cont_in_list);
4419dad6b69cSBram Moolenaar vim_free(kp);
4420071d4279SBram Moolenaar }
4421071d4279SBram Moolenaar }
4422071d4279SBram Moolenaar }
4423dad6b69cSBram Moolenaar hash_clear(ht);
4424dad6b69cSBram Moolenaar hash_init(ht);
4425dad6b69cSBram Moolenaar }
4426071d4279SBram Moolenaar
4427071d4279SBram Moolenaar /*
4428071d4279SBram Moolenaar * Add a keyword to the list of keywords.
4429071d4279SBram Moolenaar */
4430071d4279SBram Moolenaar static void
add_keyword(char_u * name,int id,int flags,short * cont_in_list,short * next_list,int conceal_char)4431764b23c8SBram Moolenaar add_keyword(
44320d6f5d97SBram Moolenaar char_u *name, // name of keyword
44330d6f5d97SBram Moolenaar int id, // group ID for this keyword
44340d6f5d97SBram Moolenaar int flags, // flags for this keyword
44350d6f5d97SBram Moolenaar short *cont_in_list, // containedin for this keyword
44360d6f5d97SBram Moolenaar short *next_list, // nextgroup for this keyword
4437764b23c8SBram Moolenaar int conceal_char)
4438071d4279SBram Moolenaar {
4439dad6b69cSBram Moolenaar keyentry_T *kp;
4440dad6b69cSBram Moolenaar hashtab_T *ht;
4441dad6b69cSBram Moolenaar hashitem_T *hi;
44426ac5429dSBram Moolenaar char_u *name_ic;
4443dad6b69cSBram Moolenaar long_u hash;
44446ac5429dSBram Moolenaar char_u name_folded[MAXKEYWLEN + 1];
4445071d4279SBram Moolenaar
4446860cae1cSBram Moolenaar if (curwin->w_s->b_syn_ic)
44476ac5429dSBram Moolenaar name_ic = str_foldcase(name, (int)STRLEN(name),
44486ac5429dSBram Moolenaar name_folded, MAXKEYWLEN + 1);
44496ac5429dSBram Moolenaar else
4450071d4279SBram Moolenaar name_ic = name;
445147ed553fSBram Moolenaar kp = alloc(offsetof(keyentry_T, keyword) + STRLEN(name_ic) + 1);
4452dad6b69cSBram Moolenaar if (kp == NULL)
4453071d4279SBram Moolenaar return;
4454dad6b69cSBram Moolenaar STRCPY(kp->keyword, name_ic);
4455dad6b69cSBram Moolenaar kp->k_syn.id = id;
4456dad6b69cSBram Moolenaar kp->k_syn.inc_tag = current_syn_inc_tag;
4457dad6b69cSBram Moolenaar kp->flags = flags;
4458860cae1cSBram Moolenaar kp->k_char = conceal_char;
4459dad6b69cSBram Moolenaar kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
4460071d4279SBram Moolenaar if (cont_in_list != NULL)
4461860cae1cSBram Moolenaar curwin->w_s->b_syn_containedin = TRUE;
4462dad6b69cSBram Moolenaar kp->next_list = copy_id_list(next_list);
4463071d4279SBram Moolenaar
4464860cae1cSBram Moolenaar if (curwin->w_s->b_syn_ic)
4465860cae1cSBram Moolenaar ht = &curwin->w_s->b_keywtab_ic;
4466071d4279SBram Moolenaar else
4467860cae1cSBram Moolenaar ht = &curwin->w_s->b_keywtab;
4468071d4279SBram Moolenaar
4469dad6b69cSBram Moolenaar hash = hash_hash(kp->keyword);
4470dad6b69cSBram Moolenaar hi = hash_lookup(ht, kp->keyword, hash);
4471dad6b69cSBram Moolenaar if (HASHITEM_EMPTY(hi))
4472071d4279SBram Moolenaar {
44730d6f5d97SBram Moolenaar // new keyword, add to hashtable
4474dad6b69cSBram Moolenaar kp->ke_next = NULL;
4475dad6b69cSBram Moolenaar hash_add_item(ht, hi, kp->keyword, hash);
4476071d4279SBram Moolenaar }
4477dad6b69cSBram Moolenaar else
4478071d4279SBram Moolenaar {
44790d6f5d97SBram Moolenaar // keyword already exists, prepend to list
4480dad6b69cSBram Moolenaar kp->ke_next = HI2KE(hi);
4481dad6b69cSBram Moolenaar hi->hi_key = KE2HIKEY(kp);
4482071d4279SBram Moolenaar }
4483071d4279SBram Moolenaar }
4484071d4279SBram Moolenaar
4485071d4279SBram Moolenaar /*
4486071d4279SBram Moolenaar * Get the start and end of the group name argument.
4487071d4279SBram Moolenaar * Return a pointer to the first argument.
4488071d4279SBram Moolenaar * Return NULL if the end of the command was found instead of further args.
4489071d4279SBram Moolenaar */
4490071d4279SBram Moolenaar static char_u *
get_group_name(char_u * arg,char_u ** name_end)4491764b23c8SBram Moolenaar get_group_name(
44920d6f5d97SBram Moolenaar char_u *arg, // start of the argument
44930d6f5d97SBram Moolenaar char_u **name_end) // pointer to end of the name
4494071d4279SBram Moolenaar {
4495071d4279SBram Moolenaar char_u *rest;
4496071d4279SBram Moolenaar
4497071d4279SBram Moolenaar *name_end = skiptowhite(arg);
4498071d4279SBram Moolenaar rest = skipwhite(*name_end);
4499071d4279SBram Moolenaar
4500071d4279SBram Moolenaar /*
4501071d4279SBram Moolenaar * Check if there are enough arguments. The first argument may be a
4502071d4279SBram Moolenaar * pattern, where '|' is allowed, so only check for NUL.
4503071d4279SBram Moolenaar */
4504071d4279SBram Moolenaar if (ends_excmd(*arg) || *rest == NUL)
4505071d4279SBram Moolenaar return NULL;
4506071d4279SBram Moolenaar return rest;
4507071d4279SBram Moolenaar }
4508071d4279SBram Moolenaar
4509071d4279SBram Moolenaar /*
4510071d4279SBram Moolenaar * Check for syntax command option arguments.
4511071d4279SBram Moolenaar * This can be called at any place in the list of arguments, and just picks
4512071d4279SBram Moolenaar * out the arguments that are known. Can be called several times in a row to
4513071d4279SBram Moolenaar * collect all options in between other arguments.
4514071d4279SBram Moolenaar * Return a pointer to the next argument (which isn't an option).
4515071d4279SBram Moolenaar * Return NULL for any error;
4516071d4279SBram Moolenaar */
4517071d4279SBram Moolenaar static char_u *
get_syn_options(char_u * start,syn_opt_arg_T * opt,int * conceal_char UNUSED,int skip)4518764b23c8SBram Moolenaar get_syn_options(
45191966c248SBram Moolenaar char_u *start, // next argument to be checked
45200d6f5d97SBram Moolenaar syn_opt_arg_T *opt, // various things
4521de318c5cSBram Moolenaar int *conceal_char UNUSED,
45220d6f5d97SBram Moolenaar int skip) // TRUE if skipping over command
4523071d4279SBram Moolenaar {
45241966c248SBram Moolenaar char_u *arg = start;
4525071d4279SBram Moolenaar char_u *gname_start, *gname;
4526071d4279SBram Moolenaar int syn_id;
4527071d4279SBram Moolenaar int len;
45286ac5429dSBram Moolenaar char *p;
4529071d4279SBram Moolenaar int i;
4530071d4279SBram Moolenaar int fidx;
4531071d4279SBram Moolenaar static struct flag
4532071d4279SBram Moolenaar {
4533071d4279SBram Moolenaar char *name;
45346ac5429dSBram Moolenaar int argtype;
45356ac5429dSBram Moolenaar int flags;
45366ac5429dSBram Moolenaar } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
45376ac5429dSBram Moolenaar {"oOnNeElLiInNeE", 0, HL_ONELINE},
45386ac5429dSBram Moolenaar {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
45396ac5429dSBram Moolenaar {"eExXtTeEnNdD", 0, HL_EXTEND},
45406ac5429dSBram Moolenaar {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
45416ac5429dSBram Moolenaar {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
45426ac5429dSBram Moolenaar {"sSkKiIpPnNlL", 0, HL_SKIPNL},
45436ac5429dSBram Moolenaar {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
45446ac5429dSBram Moolenaar {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
45456ac5429dSBram Moolenaar {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
45466ac5429dSBram Moolenaar {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
45476ac5429dSBram Moolenaar {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
45486ac5429dSBram Moolenaar {"fFoOlLdD", 0, HL_FOLD},
4549860cae1cSBram Moolenaar {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4550860cae1cSBram Moolenaar {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4551860cae1cSBram Moolenaar {"cCcChHaArR", 11, 0},
45526ac5429dSBram Moolenaar {"cCoOnNtTaAiInNsS", 1, 0},
45536ac5429dSBram Moolenaar {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
45546ac5429dSBram Moolenaar {"nNeExXtTgGrRoOuUpP", 3, 0},
4555071d4279SBram Moolenaar };
45566ac5429dSBram Moolenaar static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
4557071d4279SBram Moolenaar
45580d6f5d97SBram Moolenaar if (arg == NULL) // already detected error
4559071d4279SBram Moolenaar return NULL;
4560071d4279SBram Moolenaar
4561860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
4562860cae1cSBram Moolenaar if (curwin->w_s->b_syn_conceal)
4563860cae1cSBram Moolenaar opt->flags |= HL_CONCEAL;
4564860cae1cSBram Moolenaar #endif
4565860cae1cSBram Moolenaar
4566071d4279SBram Moolenaar for (;;)
4567071d4279SBram Moolenaar {
45686ac5429dSBram Moolenaar /*
45696ac5429dSBram Moolenaar * This is used very often when a large number of keywords is defined.
45706ac5429dSBram Moolenaar * Need to skip quickly when no option name is found.
45716ac5429dSBram Moolenaar * Also avoid tolower(), it's slow.
45726ac5429dSBram Moolenaar */
45736ac5429dSBram Moolenaar if (strchr(first_letters, *arg) == NULL)
4574071d4279SBram Moolenaar break;
4575071d4279SBram Moolenaar
4576eeec2548SK.Takata for (fidx = ARRAY_LENGTH(flagtab); --fidx >= 0; )
4577071d4279SBram Moolenaar {
45786ac5429dSBram Moolenaar p = flagtab[fidx].name;
45796ac5429dSBram Moolenaar for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
45806ac5429dSBram Moolenaar if (arg[len] != p[i] && arg[len] != p[i + 1])
45816ac5429dSBram Moolenaar break;
45821c465444SBram Moolenaar if (p[i] == NUL && (VIM_ISWHITE(arg[len])
45836ac5429dSBram Moolenaar || (flagtab[fidx].argtype > 0
45846ac5429dSBram Moolenaar ? arg[len] == '='
45851966c248SBram Moolenaar : ends_excmd2(start, arg + len))))
4586071d4279SBram Moolenaar {
45876ac5429dSBram Moolenaar if (opt->keyword
45886ac5429dSBram Moolenaar && (flagtab[fidx].flags == HL_DISPLAY
45896ac5429dSBram Moolenaar || flagtab[fidx].flags == HL_FOLD
45906ac5429dSBram Moolenaar || flagtab[fidx].flags == HL_EXTEND))
45910d6f5d97SBram Moolenaar // treat "display", "fold" and "extend" as a keyword
4592071d4279SBram Moolenaar fidx = -1;
4593071d4279SBram Moolenaar break;
4594071d4279SBram Moolenaar }
45956ac5429dSBram Moolenaar }
45960d6f5d97SBram Moolenaar if (fidx < 0) // no match found
45976ac5429dSBram Moolenaar break;
4598071d4279SBram Moolenaar
45996ac5429dSBram Moolenaar if (flagtab[fidx].argtype == 1)
46006ac5429dSBram Moolenaar {
46016ac5429dSBram Moolenaar if (!opt->has_cont_list)
46026ac5429dSBram Moolenaar {
4603f9e3e09fSBram Moolenaar emsg(_("E395: contains argument not accepted here"));
46046ac5429dSBram Moolenaar return NULL;
46056ac5429dSBram Moolenaar }
4606de318c5cSBram Moolenaar if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
46076ac5429dSBram Moolenaar return NULL;
46086ac5429dSBram Moolenaar }
46096ac5429dSBram Moolenaar else if (flagtab[fidx].argtype == 2)
46106ac5429dSBram Moolenaar {
4611de318c5cSBram Moolenaar if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
46126ac5429dSBram Moolenaar return NULL;
46136ac5429dSBram Moolenaar }
46146ac5429dSBram Moolenaar else if (flagtab[fidx].argtype == 3)
46156ac5429dSBram Moolenaar {
4616de318c5cSBram Moolenaar if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
46176ac5429dSBram Moolenaar return NULL;
46186ac5429dSBram Moolenaar }
4619860cae1cSBram Moolenaar else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4620860cae1cSBram Moolenaar {
46210d6f5d97SBram Moolenaar // cchar=?
4622860cae1cSBram Moolenaar if (has_mbyte)
4623860cae1cSBram Moolenaar {
4624860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
4625860cae1cSBram Moolenaar *conceal_char = mb_ptr2char(arg + 6);
4626860cae1cSBram Moolenaar #endif
4627860cae1cSBram Moolenaar arg += mb_ptr2len(arg + 6) - 1;
4628860cae1cSBram Moolenaar }
4629860cae1cSBram Moolenaar else
463056be9500SBram Moolenaar {
4631860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
4632860cae1cSBram Moolenaar *conceal_char = arg[6];
4633860cae1cSBram Moolenaar #else
4634860cae1cSBram Moolenaar ;
4635860cae1cSBram Moolenaar #endif
463656be9500SBram Moolenaar }
46374124e723SBram Moolenaar #ifdef FEAT_CONCEAL
46384124e723SBram Moolenaar if (!vim_isprintc_strict(*conceal_char))
46394124e723SBram Moolenaar {
4640f9e3e09fSBram Moolenaar emsg(_("E844: invalid cchar value"));
46414124e723SBram Moolenaar return NULL;
46424124e723SBram Moolenaar }
46434124e723SBram Moolenaar #endif
4644860cae1cSBram Moolenaar arg = skipwhite(arg + 7);
4645860cae1cSBram Moolenaar }
46466ac5429dSBram Moolenaar else
46476ac5429dSBram Moolenaar {
46486ac5429dSBram Moolenaar opt->flags |= flagtab[fidx].flags;
4649071d4279SBram Moolenaar arg = skipwhite(arg + len);
4650071d4279SBram Moolenaar
46516ac5429dSBram Moolenaar if (flagtab[fidx].flags == HL_SYNC_HERE
46526ac5429dSBram Moolenaar || flagtab[fidx].flags == HL_SYNC_THERE)
4653071d4279SBram Moolenaar {
46546ac5429dSBram Moolenaar if (opt->sync_idx == NULL)
4655071d4279SBram Moolenaar {
4656f9e3e09fSBram Moolenaar emsg(_("E393: group[t]here not accepted here"));
4657071d4279SBram Moolenaar return NULL;
4658071d4279SBram Moolenaar }
4659071d4279SBram Moolenaar gname_start = arg;
4660071d4279SBram Moolenaar arg = skiptowhite(arg);
4661071d4279SBram Moolenaar if (gname_start == arg)
4662071d4279SBram Moolenaar return NULL;
466371ccd03eSBram Moolenaar gname = vim_strnsave(gname_start, arg - gname_start);
4664071d4279SBram Moolenaar if (gname == NULL)
4665071d4279SBram Moolenaar return NULL;
4666071d4279SBram Moolenaar if (STRCMP(gname, "NONE") == 0)
46676ac5429dSBram Moolenaar *opt->sync_idx = NONE_IDX;
4668071d4279SBram Moolenaar else
4669071d4279SBram Moolenaar {
4670071d4279SBram Moolenaar syn_id = syn_name2id(gname);
4671860cae1cSBram Moolenaar for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4672860cae1cSBram Moolenaar if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
467371ccd03eSBram Moolenaar && SYN_ITEMS(curwin->w_s)[i].sp_type
467471ccd03eSBram Moolenaar == SPTYPE_START)
4675071d4279SBram Moolenaar {
46766ac5429dSBram Moolenaar *opt->sync_idx = i;
4677071d4279SBram Moolenaar break;
4678071d4279SBram Moolenaar }
4679071d4279SBram Moolenaar if (i < 0)
4680071d4279SBram Moolenaar {
4681f9e3e09fSBram Moolenaar semsg(_("E394: Didn't find region item for %s"), gname);
4682071d4279SBram Moolenaar vim_free(gname);
4683071d4279SBram Moolenaar return NULL;
4684071d4279SBram Moolenaar }
4685071d4279SBram Moolenaar }
4686071d4279SBram Moolenaar
4687071d4279SBram Moolenaar vim_free(gname);
4688071d4279SBram Moolenaar arg = skipwhite(arg);
4689071d4279SBram Moolenaar }
4690071d4279SBram Moolenaar #ifdef FEAT_FOLDING
46916ac5429dSBram Moolenaar else if (flagtab[fidx].flags == HL_FOLD
4692071d4279SBram Moolenaar && foldmethodIsSyntax(curwin))
46930d6f5d97SBram Moolenaar // Need to update folds later.
4694071d4279SBram Moolenaar foldUpdateAll(curwin);
4695071d4279SBram Moolenaar #endif
4696071d4279SBram Moolenaar }
4697071d4279SBram Moolenaar }
4698071d4279SBram Moolenaar
4699071d4279SBram Moolenaar return arg;
4700071d4279SBram Moolenaar }
4701071d4279SBram Moolenaar
4702071d4279SBram Moolenaar /*
4703071d4279SBram Moolenaar * Adjustments to syntax item when declared in a ":syn include"'d file.
4704071d4279SBram Moolenaar * Set the contained flag, and if the item is not already contained, add it
4705071d4279SBram Moolenaar * to the specified top-level group, if any.
4706071d4279SBram Moolenaar */
4707071d4279SBram Moolenaar static void
syn_incl_toplevel(int id,int * flagsp)4708764b23c8SBram Moolenaar syn_incl_toplevel(int id, int *flagsp)
4709071d4279SBram Moolenaar {
4710860cae1cSBram Moolenaar if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
4711071d4279SBram Moolenaar return;
4712071d4279SBram Moolenaar *flagsp |= HL_CONTAINED;
4713860cae1cSBram Moolenaar if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
4714071d4279SBram Moolenaar {
47150d6f5d97SBram Moolenaar // We have to alloc this, because syn_combine_list() will free it.
4716c799fe20SBram Moolenaar short *grp_list = ALLOC_MULT(short, 2);
4717860cae1cSBram Moolenaar int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
4718071d4279SBram Moolenaar
4719071d4279SBram Moolenaar if (grp_list != NULL)
4720071d4279SBram Moolenaar {
4721071d4279SBram Moolenaar grp_list[0] = id;
4722071d4279SBram Moolenaar grp_list[1] = 0;
4723c799fe20SBram Moolenaar syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list,
4724c799fe20SBram Moolenaar &grp_list, CLUSTER_ADD);
4725071d4279SBram Moolenaar }
4726071d4279SBram Moolenaar }
4727071d4279SBram Moolenaar }
4728071d4279SBram Moolenaar
4729071d4279SBram Moolenaar /*
4730071d4279SBram Moolenaar * Handle ":syntax include [@{group-name}] filename" command.
4731071d4279SBram Moolenaar */
4732071d4279SBram Moolenaar static void
syn_cmd_include(exarg_T * eap,int syncing UNUSED)4733764b23c8SBram Moolenaar syn_cmd_include(exarg_T *eap, int syncing UNUSED)
4734071d4279SBram Moolenaar {
4735071d4279SBram Moolenaar char_u *arg = eap->arg;
4736071d4279SBram Moolenaar int sgl_id = 1;
4737071d4279SBram Moolenaar char_u *group_name_end;
4738071d4279SBram Moolenaar char_u *rest;
4739f9e3e09fSBram Moolenaar char *errormsg = NULL;
4740071d4279SBram Moolenaar int prev_toplvl_grp;
4741071d4279SBram Moolenaar int prev_syn_inc_tag;
4742071d4279SBram Moolenaar int source = FALSE;
4743071d4279SBram Moolenaar
4744071d4279SBram Moolenaar eap->nextcmd = find_nextcmd(arg);
4745071d4279SBram Moolenaar if (eap->skip)
4746071d4279SBram Moolenaar return;
4747071d4279SBram Moolenaar
4748071d4279SBram Moolenaar if (arg[0] == '@')
4749071d4279SBram Moolenaar {
4750071d4279SBram Moolenaar ++arg;
4751071d4279SBram Moolenaar rest = get_group_name(arg, &group_name_end);
4752071d4279SBram Moolenaar if (rest == NULL)
4753071d4279SBram Moolenaar {
4754f9e3e09fSBram Moolenaar emsg(_("E397: Filename required"));
4755071d4279SBram Moolenaar return;
4756071d4279SBram Moolenaar }
4757071d4279SBram Moolenaar sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
475842431a7aSBram Moolenaar if (sgl_id == 0)
475942431a7aSBram Moolenaar return;
47600d6f5d97SBram Moolenaar // separate_nextcmd() and expand_filename() depend on this
4761071d4279SBram Moolenaar eap->arg = rest;
4762071d4279SBram Moolenaar }
4763071d4279SBram Moolenaar
4764071d4279SBram Moolenaar /*
4765071d4279SBram Moolenaar * Everything that's left, up to the next command, should be the
4766071d4279SBram Moolenaar * filename to include.
4767071d4279SBram Moolenaar */
47688071cb2cSBram Moolenaar eap->argt |= (EX_XFILE | EX_NOSPC);
4769071d4279SBram Moolenaar separate_nextcmd(eap);
4770071d4279SBram Moolenaar if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4771071d4279SBram Moolenaar {
47720d6f5d97SBram Moolenaar // For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
47730d6f5d97SBram Moolenaar // file. Need to expand the file name first. In other cases
47740d6f5d97SBram Moolenaar // ":runtime!" is used.
4775071d4279SBram Moolenaar source = TRUE;
4776071d4279SBram Moolenaar if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4777071d4279SBram Moolenaar {
4778071d4279SBram Moolenaar if (errormsg != NULL)
4779f9e3e09fSBram Moolenaar emsg(errormsg);
4780071d4279SBram Moolenaar return;
4781071d4279SBram Moolenaar }
4782071d4279SBram Moolenaar }
4783071d4279SBram Moolenaar
4784071d4279SBram Moolenaar /*
4785071d4279SBram Moolenaar * Save and restore the existing top-level grouplist id and ":syn
4786071d4279SBram Moolenaar * include" tag around the actual inclusion.
4787071d4279SBram Moolenaar */
478842431a7aSBram Moolenaar if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
478942431a7aSBram Moolenaar {
4790f9e3e09fSBram Moolenaar emsg(_("E847: Too many syntax includes"));
479142431a7aSBram Moolenaar return;
479242431a7aSBram Moolenaar }
4793071d4279SBram Moolenaar prev_syn_inc_tag = current_syn_inc_tag;
4794071d4279SBram Moolenaar current_syn_inc_tag = ++running_syn_inc_tag;
4795860cae1cSBram Moolenaar prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4796860cae1cSBram Moolenaar curwin->w_s->b_syn_topgrp = sgl_id;
47978a7d6542SBram Moolenaar if (source ? do_source(eap->arg, FALSE, DOSO_NONE, NULL) == FAIL
47987f8989ddSBram Moolenaar : source_runtime(eap->arg, DIP_ALL) == FAIL)
4799f9e3e09fSBram Moolenaar semsg(_(e_notopen), eap->arg);
4800860cae1cSBram Moolenaar curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
4801071d4279SBram Moolenaar current_syn_inc_tag = prev_syn_inc_tag;
4802071d4279SBram Moolenaar }
4803071d4279SBram Moolenaar
4804071d4279SBram Moolenaar /*
4805071d4279SBram Moolenaar * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4806071d4279SBram Moolenaar */
4807071d4279SBram Moolenaar static void
syn_cmd_keyword(exarg_T * eap,int syncing UNUSED)4808764b23c8SBram Moolenaar syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
4809071d4279SBram Moolenaar {
4810071d4279SBram Moolenaar char_u *arg = eap->arg;
4811071d4279SBram Moolenaar char_u *group_name_end;
4812071d4279SBram Moolenaar int syn_id;
4813071d4279SBram Moolenaar char_u *rest;
481442431a7aSBram Moolenaar char_u *keyword_copy = NULL;
4815071d4279SBram Moolenaar char_u *p;
48166ac5429dSBram Moolenaar char_u *kw;
48176ac5429dSBram Moolenaar syn_opt_arg_T syn_opt_arg;
48186ac5429dSBram Moolenaar int cnt;
4819860cae1cSBram Moolenaar int conceal_char = NUL;
4820071d4279SBram Moolenaar
4821071d4279SBram Moolenaar rest = get_group_name(arg, &group_name_end);
4822071d4279SBram Moolenaar
4823071d4279SBram Moolenaar if (rest != NULL)
4824071d4279SBram Moolenaar {
4825de318c5cSBram Moolenaar if (eap->skip)
4826de318c5cSBram Moolenaar syn_id = -1;
4827de318c5cSBram Moolenaar else
4828071d4279SBram Moolenaar syn_id = syn_check_group(arg, (int)(group_name_end - arg));
482942431a7aSBram Moolenaar if (syn_id != 0)
48300d6f5d97SBram Moolenaar // allocate a buffer, for removing backslashes in the keyword
4831964b3746SBram Moolenaar keyword_copy = alloc(STRLEN(rest) + 1);
4832071d4279SBram Moolenaar if (keyword_copy != NULL)
4833071d4279SBram Moolenaar {
48346ac5429dSBram Moolenaar syn_opt_arg.flags = 0;
48356ac5429dSBram Moolenaar syn_opt_arg.keyword = TRUE;
48366ac5429dSBram Moolenaar syn_opt_arg.sync_idx = NULL;
48376ac5429dSBram Moolenaar syn_opt_arg.has_cont_list = FALSE;
48386ac5429dSBram Moolenaar syn_opt_arg.cont_in_list = NULL;
48396ac5429dSBram Moolenaar syn_opt_arg.next_list = NULL;
48406ac5429dSBram Moolenaar
4841071d4279SBram Moolenaar /*
4842071d4279SBram Moolenaar * The options given apply to ALL keywords, so all options must be
4843071d4279SBram Moolenaar * found before keywords can be created.
48446ac5429dSBram Moolenaar * 1: collect the options and copy the keywords to keyword_copy.
4845071d4279SBram Moolenaar */
48466ac5429dSBram Moolenaar cnt = 0;
48476ac5429dSBram Moolenaar p = keyword_copy;
48481966c248SBram Moolenaar for ( ; rest != NULL && !ends_excmd2(eap->arg, rest);
48491966c248SBram Moolenaar rest = skipwhite(rest))
4850071d4279SBram Moolenaar {
4851de318c5cSBram Moolenaar rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4852de318c5cSBram Moolenaar eap->skip);
48531966c248SBram Moolenaar if (rest == NULL || ends_excmd2(eap->arg, rest))
4854071d4279SBram Moolenaar break;
48550d6f5d97SBram Moolenaar // Copy the keyword, removing backslashes, and add a NUL.
48561c465444SBram Moolenaar while (*rest != NUL && !VIM_ISWHITE(*rest))
4857071d4279SBram Moolenaar {
4858071d4279SBram Moolenaar if (*rest == '\\' && rest[1] != NUL)
4859071d4279SBram Moolenaar ++rest;
4860071d4279SBram Moolenaar *p++ = *rest++;
4861071d4279SBram Moolenaar }
48626ac5429dSBram Moolenaar *p++ = NUL;
48636ac5429dSBram Moolenaar ++cnt;
48646ac5429dSBram Moolenaar }
48656ac5429dSBram Moolenaar
48666ac5429dSBram Moolenaar if (!eap->skip)
4867071d4279SBram Moolenaar {
48680d6f5d97SBram Moolenaar // Adjust flags for use of ":syn include".
48696ac5429dSBram Moolenaar syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
48706ac5429dSBram Moolenaar
48716ac5429dSBram Moolenaar /*
48726ac5429dSBram Moolenaar * 2: Add an entry for each keyword.
48736ac5429dSBram Moolenaar */
48746ac5429dSBram Moolenaar for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
48756ac5429dSBram Moolenaar {
48766ac5429dSBram Moolenaar for (p = vim_strchr(kw, '['); ; )
4877071d4279SBram Moolenaar {
4878071d4279SBram Moolenaar if (p != NULL)
4879071d4279SBram Moolenaar *p = NUL;
48806ac5429dSBram Moolenaar add_keyword(kw, syn_id, syn_opt_arg.flags,
48816ac5429dSBram Moolenaar syn_opt_arg.cont_in_list,
4882860cae1cSBram Moolenaar syn_opt_arg.next_list, conceal_char);
488326a60b45SBram Moolenaar if (p == NULL)
4884071d4279SBram Moolenaar break;
488526a60b45SBram Moolenaar if (p[1] == NUL)
488626a60b45SBram Moolenaar {
4887f9e3e09fSBram Moolenaar semsg(_("E789: Missing ']': %s"), kw);
48881560d070SBram Moolenaar goto error;
488926a60b45SBram Moolenaar }
489026a60b45SBram Moolenaar if (p[1] == ']')
489126a60b45SBram Moolenaar {
48921560d070SBram Moolenaar if (p[2] != NUL)
48931560d070SBram Moolenaar {
4894f9e3e09fSBram Moolenaar semsg(_("E890: trailing char after ']': %s]%s"),
48951560d070SBram Moolenaar kw, &p[2]);
48961560d070SBram Moolenaar goto error;
48971560d070SBram Moolenaar }
48980d6f5d97SBram Moolenaar kw = p + 1; // skip over the "]"
489926a60b45SBram Moolenaar break;
490026a60b45SBram Moolenaar }
4901071d4279SBram Moolenaar if (has_mbyte)
4902071d4279SBram Moolenaar {
49030fa313a7SBram Moolenaar int l = (*mb_ptr2len)(p + 1);
4904071d4279SBram Moolenaar
4905071d4279SBram Moolenaar mch_memmove(p, p + 1, l);
4906071d4279SBram Moolenaar p += l;
4907071d4279SBram Moolenaar }
4908071d4279SBram Moolenaar else
4909071d4279SBram Moolenaar {
4910071d4279SBram Moolenaar p[0] = p[1];
4911071d4279SBram Moolenaar ++p;
4912071d4279SBram Moolenaar }
4913071d4279SBram Moolenaar }
4914071d4279SBram Moolenaar }
4915071d4279SBram Moolenaar }
49161560d070SBram Moolenaar error:
4917071d4279SBram Moolenaar vim_free(keyword_copy);
491826a60b45SBram Moolenaar vim_free(syn_opt_arg.cont_in_list);
491926a60b45SBram Moolenaar vim_free(syn_opt_arg.next_list);
4920071d4279SBram Moolenaar }
4921071d4279SBram Moolenaar }
4922071d4279SBram Moolenaar
4923071d4279SBram Moolenaar if (rest != NULL)
492463b91736SBram Moolenaar set_nextcmd(eap, rest);
4925071d4279SBram Moolenaar else
4926f9e3e09fSBram Moolenaar semsg(_(e_invarg2), arg);
4927071d4279SBram Moolenaar
49281c8f93ffSBram Moolenaar redraw_curbuf_later(SOME_VALID);
49290d6f5d97SBram Moolenaar syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
4930071d4279SBram Moolenaar }
4931071d4279SBram Moolenaar
4932071d4279SBram Moolenaar /*
4933071d4279SBram Moolenaar * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4934071d4279SBram Moolenaar *
4935071d4279SBram Moolenaar * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4936071d4279SBram Moolenaar */
4937071d4279SBram Moolenaar static void
syn_cmd_match(exarg_T * eap,int syncing)4938764b23c8SBram Moolenaar syn_cmd_match(
4939764b23c8SBram Moolenaar exarg_T *eap,
49400d6f5d97SBram Moolenaar int syncing) // TRUE for ":syntax sync match .. "
4941071d4279SBram Moolenaar {
4942071d4279SBram Moolenaar char_u *arg = eap->arg;
4943071d4279SBram Moolenaar char_u *group_name_end;
4944071d4279SBram Moolenaar char_u *rest;
49450d6f5d97SBram Moolenaar synpat_T item; // the item found in the line
4946071d4279SBram Moolenaar int syn_id;
4947071d4279SBram Moolenaar int idx;
49486ac5429dSBram Moolenaar syn_opt_arg_T syn_opt_arg;
4949071d4279SBram Moolenaar int sync_idx = 0;
4950860cae1cSBram Moolenaar int conceal_char = NUL;
49511966c248SBram Moolenaar int orig_called_emsg = called_emsg;
4952071d4279SBram Moolenaar
49530d6f5d97SBram Moolenaar // Isolate the group name, check for validity
4954071d4279SBram Moolenaar rest = get_group_name(arg, &group_name_end);
4955071d4279SBram Moolenaar
49560d6f5d97SBram Moolenaar // Get options before the pattern
49576ac5429dSBram Moolenaar syn_opt_arg.flags = 0;
49586ac5429dSBram Moolenaar syn_opt_arg.keyword = FALSE;
49596ac5429dSBram Moolenaar syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
49606ac5429dSBram Moolenaar syn_opt_arg.has_cont_list = TRUE;
49616ac5429dSBram Moolenaar syn_opt_arg.cont_list = NULL;
49626ac5429dSBram Moolenaar syn_opt_arg.cont_in_list = NULL;
49636ac5429dSBram Moolenaar syn_opt_arg.next_list = NULL;
4964de318c5cSBram Moolenaar rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
4965071d4279SBram Moolenaar
49660d6f5d97SBram Moolenaar // get the pattern.
4967071d4279SBram Moolenaar init_syn_patterns();
4968a80faa89SBram Moolenaar CLEAR_FIELD(item);
4969071d4279SBram Moolenaar rest = get_syn_pattern(rest, &item);
49706ac5429dSBram Moolenaar if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
49716ac5429dSBram Moolenaar syn_opt_arg.flags |= HL_HAS_EOL;
4972071d4279SBram Moolenaar
49730d6f5d97SBram Moolenaar // Get options after the pattern
4974de318c5cSBram Moolenaar rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
4975071d4279SBram Moolenaar
49760d6f5d97SBram Moolenaar if (rest != NULL) // all arguments are valid
4977071d4279SBram Moolenaar {
4978071d4279SBram Moolenaar /*
4979071d4279SBram Moolenaar * Check for trailing command and illegal trailing arguments.
4980071d4279SBram Moolenaar */
498163b91736SBram Moolenaar set_nextcmd(eap, rest);
49821966c248SBram Moolenaar if (!ends_excmd2(eap->cmd, rest) || eap->skip)
4983071d4279SBram Moolenaar rest = NULL;
4984860cae1cSBram Moolenaar else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
4985071d4279SBram Moolenaar && (syn_id = syn_check_group(arg,
4986071d4279SBram Moolenaar (int)(group_name_end - arg))) != 0)
4987071d4279SBram Moolenaar {
49886ac5429dSBram Moolenaar syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4989071d4279SBram Moolenaar /*
4990071d4279SBram Moolenaar * Store the pattern in the syn_items list
4991071d4279SBram Moolenaar */
4992860cae1cSBram Moolenaar idx = curwin->w_s->b_syn_patterns.ga_len;
4993860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx] = item;
4994860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4995860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4996860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4997860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4998860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4999860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5000860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5001860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
50026ac5429dSBram Moolenaar syn_opt_arg.cont_in_list;
5003860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
50046e202e52SBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
5005860cae1cSBram Moolenaar #endif
50066ac5429dSBram Moolenaar if (syn_opt_arg.cont_in_list != NULL)
5007860cae1cSBram Moolenaar curwin->w_s->b_syn_containedin = TRUE;
5008860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5009860cae1cSBram Moolenaar ++curwin->w_s->b_syn_patterns.ga_len;
5010071d4279SBram Moolenaar
50110d6f5d97SBram Moolenaar // remember that we found a match for syncing on
50126ac5429dSBram Moolenaar if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
5013860cae1cSBram Moolenaar curwin->w_s->b_syn_sync_flags |= SF_MATCH;
5014071d4279SBram Moolenaar #ifdef FEAT_FOLDING
50156ac5429dSBram Moolenaar if (syn_opt_arg.flags & HL_FOLD)
5016860cae1cSBram Moolenaar ++curwin->w_s->b_syn_folditems;
5017071d4279SBram Moolenaar #endif
5018071d4279SBram Moolenaar
50191c8f93ffSBram Moolenaar redraw_curbuf_later(SOME_VALID);
50200d6f5d97SBram Moolenaar syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
50210d6f5d97SBram Moolenaar return; // don't free the progs and patterns now
5022071d4279SBram Moolenaar }
5023071d4279SBram Moolenaar }
5024071d4279SBram Moolenaar
5025071d4279SBram Moolenaar /*
5026071d4279SBram Moolenaar * Something failed, free the allocated memory.
5027071d4279SBram Moolenaar */
5028473de61bSBram Moolenaar vim_regfree(item.sp_prog);
5029071d4279SBram Moolenaar vim_free(item.sp_pattern);
50306ac5429dSBram Moolenaar vim_free(syn_opt_arg.cont_list);
50316ac5429dSBram Moolenaar vim_free(syn_opt_arg.cont_in_list);
50326ac5429dSBram Moolenaar vim_free(syn_opt_arg.next_list);
5033071d4279SBram Moolenaar
50341966c248SBram Moolenaar if (rest == NULL && called_emsg == orig_called_emsg)
5035f9e3e09fSBram Moolenaar semsg(_(e_invarg2), arg);
5036071d4279SBram Moolenaar }
5037071d4279SBram Moolenaar
5038071d4279SBram Moolenaar /*
5039071d4279SBram Moolenaar * Handle ":syntax region {group-name} [matchgroup={group-name}]
5040071d4279SBram Moolenaar * start {start} .. [skip {skip}] end {end} .. [{options}]".
5041071d4279SBram Moolenaar */
5042071d4279SBram Moolenaar static void
syn_cmd_region(exarg_T * eap,int syncing)5043764b23c8SBram Moolenaar syn_cmd_region(
5044764b23c8SBram Moolenaar exarg_T *eap,
50450d6f5d97SBram Moolenaar int syncing) // TRUE for ":syntax sync region .."
5046071d4279SBram Moolenaar {
5047071d4279SBram Moolenaar char_u *arg = eap->arg;
5048071d4279SBram Moolenaar char_u *group_name_end;
50490d6f5d97SBram Moolenaar char_u *rest; // next arg, NULL on error
5050071d4279SBram Moolenaar char_u *key_end;
5051071d4279SBram Moolenaar char_u *key = NULL;
5052071d4279SBram Moolenaar char_u *p;
5053071d4279SBram Moolenaar int item;
5054071d4279SBram Moolenaar #define ITEM_START 0
5055071d4279SBram Moolenaar #define ITEM_SKIP 1
5056071d4279SBram Moolenaar #define ITEM_END 2
5057071d4279SBram Moolenaar #define ITEM_MATCHGROUP 3
5058071d4279SBram Moolenaar struct pat_ptr
5059071d4279SBram Moolenaar {
50600d6f5d97SBram Moolenaar synpat_T *pp_synp; // pointer to syn_pattern
50610d6f5d97SBram Moolenaar int pp_matchgroup_id; // matchgroup ID
50620d6f5d97SBram Moolenaar struct pat_ptr *pp_next; // pointer to next pat_ptr
5063071d4279SBram Moolenaar } *(pat_ptrs[3]);
50640d6f5d97SBram Moolenaar // patterns found in the line
5065071d4279SBram Moolenaar struct pat_ptr *ppp;
5066071d4279SBram Moolenaar struct pat_ptr *ppp_next;
50670d6f5d97SBram Moolenaar int pat_count = 0; // nr of syn_patterns found
5068071d4279SBram Moolenaar int syn_id;
5069071d4279SBram Moolenaar int matchgroup_id = 0;
50700d6f5d97SBram Moolenaar int not_enough = FALSE; // not enough arguments
50710d6f5d97SBram Moolenaar int illegal = FALSE; // illegal arguments
5072071d4279SBram Moolenaar int success = FALSE;
5073071d4279SBram Moolenaar int idx;
50746ac5429dSBram Moolenaar syn_opt_arg_T syn_opt_arg;
5075860cae1cSBram Moolenaar int conceal_char = NUL;
5076071d4279SBram Moolenaar
50770d6f5d97SBram Moolenaar // Isolate the group name, check for validity
5078071d4279SBram Moolenaar rest = get_group_name(arg, &group_name_end);
5079071d4279SBram Moolenaar
5080071d4279SBram Moolenaar pat_ptrs[0] = NULL;
5081071d4279SBram Moolenaar pat_ptrs[1] = NULL;
5082071d4279SBram Moolenaar pat_ptrs[2] = NULL;
5083071d4279SBram Moolenaar
5084071d4279SBram Moolenaar init_syn_patterns();
5085071d4279SBram Moolenaar
50866ac5429dSBram Moolenaar syn_opt_arg.flags = 0;
50876ac5429dSBram Moolenaar syn_opt_arg.keyword = FALSE;
50886ac5429dSBram Moolenaar syn_opt_arg.sync_idx = NULL;
50896ac5429dSBram Moolenaar syn_opt_arg.has_cont_list = TRUE;
50906ac5429dSBram Moolenaar syn_opt_arg.cont_list = NULL;
50916ac5429dSBram Moolenaar syn_opt_arg.cont_in_list = NULL;
50926ac5429dSBram Moolenaar syn_opt_arg.next_list = NULL;
50936ac5429dSBram Moolenaar
5094071d4279SBram Moolenaar /*
5095071d4279SBram Moolenaar * get the options, patterns and matchgroup.
5096071d4279SBram Moolenaar */
50971966c248SBram Moolenaar while (rest != NULL && !ends_excmd2(eap->cmd, rest))
5098071d4279SBram Moolenaar {
50990d6f5d97SBram Moolenaar // Check for option arguments
5100de318c5cSBram Moolenaar rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
51011966c248SBram Moolenaar if (rest == NULL || ends_excmd2(eap->cmd, rest))
5102071d4279SBram Moolenaar break;
5103071d4279SBram Moolenaar
51040d6f5d97SBram Moolenaar // must be a pattern or matchgroup then
5105071d4279SBram Moolenaar key_end = rest;
51061c465444SBram Moolenaar while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
5107071d4279SBram Moolenaar ++key_end;
5108071d4279SBram Moolenaar vim_free(key);
5109df44a27bSBram Moolenaar key = vim_strnsave_up(rest, key_end - rest);
51100d6f5d97SBram Moolenaar if (key == NULL) // out of memory
5111071d4279SBram Moolenaar {
5112071d4279SBram Moolenaar rest = NULL;
5113071d4279SBram Moolenaar break;
5114071d4279SBram Moolenaar }
5115071d4279SBram Moolenaar if (STRCMP(key, "MATCHGROUP") == 0)
5116071d4279SBram Moolenaar item = ITEM_MATCHGROUP;
5117071d4279SBram Moolenaar else if (STRCMP(key, "START") == 0)
5118071d4279SBram Moolenaar item = ITEM_START;
5119071d4279SBram Moolenaar else if (STRCMP(key, "END") == 0)
5120071d4279SBram Moolenaar item = ITEM_END;
5121071d4279SBram Moolenaar else if (STRCMP(key, "SKIP") == 0)
5122071d4279SBram Moolenaar {
51230d6f5d97SBram Moolenaar if (pat_ptrs[ITEM_SKIP] != NULL) // one skip pattern allowed
5124071d4279SBram Moolenaar {
5125071d4279SBram Moolenaar illegal = TRUE;
5126071d4279SBram Moolenaar break;
5127071d4279SBram Moolenaar }
5128071d4279SBram Moolenaar item = ITEM_SKIP;
5129071d4279SBram Moolenaar }
5130071d4279SBram Moolenaar else
5131071d4279SBram Moolenaar break;
5132071d4279SBram Moolenaar rest = skipwhite(key_end);
5133071d4279SBram Moolenaar if (*rest != '=')
5134071d4279SBram Moolenaar {
5135071d4279SBram Moolenaar rest = NULL;
5136f9e3e09fSBram Moolenaar semsg(_("E398: Missing '=': %s"), arg);
5137071d4279SBram Moolenaar break;
5138071d4279SBram Moolenaar }
5139071d4279SBram Moolenaar rest = skipwhite(rest + 1);
5140071d4279SBram Moolenaar if (*rest == NUL)
5141071d4279SBram Moolenaar {
5142071d4279SBram Moolenaar not_enough = TRUE;
5143071d4279SBram Moolenaar break;
5144071d4279SBram Moolenaar }
5145071d4279SBram Moolenaar
5146071d4279SBram Moolenaar if (item == ITEM_MATCHGROUP)
5147071d4279SBram Moolenaar {
5148071d4279SBram Moolenaar p = skiptowhite(rest);
5149071d4279SBram Moolenaar if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5150071d4279SBram Moolenaar matchgroup_id = 0;
5151071d4279SBram Moolenaar else
5152071d4279SBram Moolenaar {
5153071d4279SBram Moolenaar matchgroup_id = syn_check_group(rest, (int)(p - rest));
5154071d4279SBram Moolenaar if (matchgroup_id == 0)
5155071d4279SBram Moolenaar {
5156071d4279SBram Moolenaar illegal = TRUE;
5157071d4279SBram Moolenaar break;
5158071d4279SBram Moolenaar }
5159071d4279SBram Moolenaar }
5160071d4279SBram Moolenaar rest = skipwhite(p);
5161071d4279SBram Moolenaar }
5162071d4279SBram Moolenaar else
5163071d4279SBram Moolenaar {
5164071d4279SBram Moolenaar /*
5165071d4279SBram Moolenaar * Allocate room for a syn_pattern, and link it in the list of
5166071d4279SBram Moolenaar * syn_patterns for this item, at the start (because the list is
5167071d4279SBram Moolenaar * used from end to start).
5168071d4279SBram Moolenaar */
5169c799fe20SBram Moolenaar ppp = ALLOC_ONE(struct pat_ptr);
5170071d4279SBram Moolenaar if (ppp == NULL)
5171071d4279SBram Moolenaar {
5172071d4279SBram Moolenaar rest = NULL;
5173071d4279SBram Moolenaar break;
5174071d4279SBram Moolenaar }
5175071d4279SBram Moolenaar ppp->pp_next = pat_ptrs[item];
5176071d4279SBram Moolenaar pat_ptrs[item] = ppp;
5177c799fe20SBram Moolenaar ppp->pp_synp = ALLOC_CLEAR_ONE(synpat_T);
5178071d4279SBram Moolenaar if (ppp->pp_synp == NULL)
5179071d4279SBram Moolenaar {
5180071d4279SBram Moolenaar rest = NULL;
5181071d4279SBram Moolenaar break;
5182071d4279SBram Moolenaar }
5183071d4279SBram Moolenaar
5184071d4279SBram Moolenaar /*
5185071d4279SBram Moolenaar * Get the syntax pattern and the following offset(s).
5186071d4279SBram Moolenaar */
51870d6f5d97SBram Moolenaar // Enable the appropriate \z specials.
5188071d4279SBram Moolenaar if (item == ITEM_START)
5189071d4279SBram Moolenaar reg_do_extmatch = REX_SET;
5190071d4279SBram Moolenaar else if (item == ITEM_SKIP || item == ITEM_END)
5191071d4279SBram Moolenaar reg_do_extmatch = REX_USE;
5192071d4279SBram Moolenaar rest = get_syn_pattern(rest, ppp->pp_synp);
5193071d4279SBram Moolenaar reg_do_extmatch = 0;
5194071d4279SBram Moolenaar if (item == ITEM_END && vim_regcomp_had_eol()
51956ac5429dSBram Moolenaar && !(syn_opt_arg.flags & HL_EXCLUDENL))
5196071d4279SBram Moolenaar ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5197071d4279SBram Moolenaar ppp->pp_matchgroup_id = matchgroup_id;
5198071d4279SBram Moolenaar ++pat_count;
5199071d4279SBram Moolenaar }
5200071d4279SBram Moolenaar }
5201071d4279SBram Moolenaar vim_free(key);
5202071d4279SBram Moolenaar if (illegal || not_enough)
5203071d4279SBram Moolenaar rest = NULL;
5204071d4279SBram Moolenaar
5205071d4279SBram Moolenaar /*
5206071d4279SBram Moolenaar * Must have a "start" and "end" pattern.
5207071d4279SBram Moolenaar */
5208071d4279SBram Moolenaar if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5209071d4279SBram Moolenaar pat_ptrs[ITEM_END] == NULL))
5210071d4279SBram Moolenaar {
5211071d4279SBram Moolenaar not_enough = TRUE;
5212071d4279SBram Moolenaar rest = NULL;
5213071d4279SBram Moolenaar }
5214071d4279SBram Moolenaar
5215071d4279SBram Moolenaar if (rest != NULL)
5216071d4279SBram Moolenaar {
5217071d4279SBram Moolenaar /*
5218071d4279SBram Moolenaar * Check for trailing garbage or command.
5219071d4279SBram Moolenaar * If OK, add the item.
5220071d4279SBram Moolenaar */
522163b91736SBram Moolenaar set_nextcmd(eap, rest);
5222071d4279SBram Moolenaar if (!ends_excmd(*rest) || eap->skip)
5223071d4279SBram Moolenaar rest = NULL;
5224860cae1cSBram Moolenaar else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
5225071d4279SBram Moolenaar && (syn_id = syn_check_group(arg,
5226071d4279SBram Moolenaar (int)(group_name_end - arg))) != 0)
5227071d4279SBram Moolenaar {
52286ac5429dSBram Moolenaar syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
5229071d4279SBram Moolenaar /*
5230071d4279SBram Moolenaar * Store the start/skip/end in the syn_items list
5231071d4279SBram Moolenaar */
5232860cae1cSBram Moolenaar idx = curwin->w_s->b_syn_patterns.ga_len;
5233071d4279SBram Moolenaar for (item = ITEM_START; item <= ITEM_END; ++item)
5234071d4279SBram Moolenaar {
5235071d4279SBram Moolenaar for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5236071d4279SBram Moolenaar {
5237860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5238860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5239860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_type =
5240071d4279SBram Moolenaar (item == ITEM_START) ? SPTYPE_START :
5241071d4279SBram Moolenaar (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
5242860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5243860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
524442431a7aSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
524542431a7aSBram Moolenaar current_syn_inc_tag;
5246860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
5247071d4279SBram Moolenaar ppp->pp_matchgroup_id;
5248860cae1cSBram Moolenaar #ifdef FEAT_CONCEAL
52496e202e52SBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
5250860cae1cSBram Moolenaar #endif
5251071d4279SBram Moolenaar if (item == ITEM_START)
5252071d4279SBram Moolenaar {
5253860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
52546ac5429dSBram Moolenaar syn_opt_arg.cont_list;
5255860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
52566ac5429dSBram Moolenaar syn_opt_arg.cont_in_list;
52576ac5429dSBram Moolenaar if (syn_opt_arg.cont_in_list != NULL)
5258860cae1cSBram Moolenaar curwin->w_s->b_syn_containedin = TRUE;
5259860cae1cSBram Moolenaar SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
52606ac5429dSBram Moolenaar syn_opt_arg.next_list;
5261071d4279SBram Moolenaar }
5262860cae1cSBram Moolenaar ++curwin->w_s->b_syn_patterns.ga_len;
5263071d4279SBram Moolenaar ++idx;
5264071d4279SBram Moolenaar #ifdef FEAT_FOLDING
52656ac5429dSBram Moolenaar if (syn_opt_arg.flags & HL_FOLD)
5266860cae1cSBram Moolenaar ++curwin->w_s->b_syn_folditems;
5267071d4279SBram Moolenaar #endif
5268071d4279SBram Moolenaar }
5269071d4279SBram Moolenaar }
5270071d4279SBram Moolenaar
52711c8f93ffSBram Moolenaar redraw_curbuf_later(SOME_VALID);
52720d6f5d97SBram Moolenaar syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
52730d6f5d97SBram Moolenaar success = TRUE; // don't free the progs and patterns now
5274071d4279SBram Moolenaar }
5275071d4279SBram Moolenaar }
5276071d4279SBram Moolenaar
5277071d4279SBram Moolenaar /*
5278071d4279SBram Moolenaar * Free the allocated memory.
5279071d4279SBram Moolenaar */
5280071d4279SBram Moolenaar for (item = ITEM_START; item <= ITEM_END; ++item)
5281071d4279SBram Moolenaar for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5282071d4279SBram Moolenaar {
52834bbfb0f3SBram Moolenaar if (!success && ppp->pp_synp != NULL)
5284071d4279SBram Moolenaar {
5285473de61bSBram Moolenaar vim_regfree(ppp->pp_synp->sp_prog);
5286071d4279SBram Moolenaar vim_free(ppp->pp_synp->sp_pattern);
5287071d4279SBram Moolenaar }
5288071d4279SBram Moolenaar vim_free(ppp->pp_synp);
5289071d4279SBram Moolenaar ppp_next = ppp->pp_next;
5290071d4279SBram Moolenaar vim_free(ppp);
5291071d4279SBram Moolenaar }
5292071d4279SBram Moolenaar
5293071d4279SBram Moolenaar if (!success)
5294071d4279SBram Moolenaar {
52956ac5429dSBram Moolenaar vim_free(syn_opt_arg.cont_list);
52966ac5429dSBram Moolenaar vim_free(syn_opt_arg.cont_in_list);
52976ac5429dSBram Moolenaar vim_free(syn_opt_arg.next_list);
5298071d4279SBram Moolenaar if (not_enough)
5299f9e3e09fSBram Moolenaar semsg(_("E399: Not enough arguments: syntax region %s"), arg);
5300071d4279SBram Moolenaar else if (illegal || rest == NULL)
5301f9e3e09fSBram Moolenaar semsg(_(e_invarg2), arg);
5302071d4279SBram Moolenaar }
5303071d4279SBram Moolenaar }
5304071d4279SBram Moolenaar
5305071d4279SBram Moolenaar /*
5306071d4279SBram Moolenaar * A simple syntax group ID comparison function suitable for use in qsort()
5307071d4279SBram Moolenaar */
5308071d4279SBram Moolenaar static int
syn_compare_stub(const void * v1,const void * v2)5309764b23c8SBram Moolenaar syn_compare_stub(const void *v1, const void *v2)
5310071d4279SBram Moolenaar {
5311071d4279SBram Moolenaar const short *s1 = v1;
5312071d4279SBram Moolenaar const short *s2 = v2;
5313071d4279SBram Moolenaar
5314071d4279SBram Moolenaar return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5315071d4279SBram Moolenaar }
5316071d4279SBram Moolenaar
5317071d4279SBram Moolenaar /*
5318071d4279SBram Moolenaar * Combines lists of syntax clusters.
5319071d4279SBram Moolenaar * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5320071d4279SBram Moolenaar */
5321071d4279SBram Moolenaar static void
syn_combine_list(short ** clstr1,short ** clstr2,int list_op)5322764b23c8SBram Moolenaar syn_combine_list(short **clstr1, short **clstr2, int list_op)
5323071d4279SBram Moolenaar {
5324071d4279SBram Moolenaar int count1 = 0;
5325071d4279SBram Moolenaar int count2 = 0;
5326071d4279SBram Moolenaar short *g1;
5327071d4279SBram Moolenaar short *g2;
5328071d4279SBram Moolenaar short *clstr = NULL;
5329071d4279SBram Moolenaar int count;
5330071d4279SBram Moolenaar int round;
5331071d4279SBram Moolenaar
5332071d4279SBram Moolenaar /*
5333071d4279SBram Moolenaar * Handle degenerate cases.
5334071d4279SBram Moolenaar */
5335071d4279SBram Moolenaar if (*clstr2 == NULL)
5336071d4279SBram Moolenaar return;
5337071d4279SBram Moolenaar if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5338071d4279SBram Moolenaar {
5339071d4279SBram Moolenaar if (list_op == CLUSTER_REPLACE)
5340071d4279SBram Moolenaar vim_free(*clstr1);
5341071d4279SBram Moolenaar if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5342071d4279SBram Moolenaar *clstr1 = *clstr2;
5343071d4279SBram Moolenaar else
5344071d4279SBram Moolenaar vim_free(*clstr2);
5345071d4279SBram Moolenaar return;
5346071d4279SBram Moolenaar }
5347071d4279SBram Moolenaar
5348071d4279SBram Moolenaar for (g1 = *clstr1; *g1; g1++)
5349071d4279SBram Moolenaar ++count1;
5350071d4279SBram Moolenaar for (g2 = *clstr2; *g2; g2++)
5351071d4279SBram Moolenaar ++count2;
5352071d4279SBram Moolenaar
5353071d4279SBram Moolenaar /*
5354071d4279SBram Moolenaar * For speed purposes, sort both lists.
5355071d4279SBram Moolenaar */
5356071d4279SBram Moolenaar qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5357071d4279SBram Moolenaar qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5358071d4279SBram Moolenaar
5359071d4279SBram Moolenaar /*
5360071d4279SBram Moolenaar * We proceed in two passes; in round 1, we count the elements to place
5361071d4279SBram Moolenaar * in the new list, and in round 2, we allocate and populate the new
5362071d4279SBram Moolenaar * list. For speed, we use a mergesort-like method, adding the smaller
5363071d4279SBram Moolenaar * of the current elements in each list to the new list.
5364071d4279SBram Moolenaar */
5365071d4279SBram Moolenaar for (round = 1; round <= 2; round++)
5366071d4279SBram Moolenaar {
5367071d4279SBram Moolenaar g1 = *clstr1;
5368071d4279SBram Moolenaar g2 = *clstr2;
5369071d4279SBram Moolenaar count = 0;
5370071d4279SBram Moolenaar
5371071d4279SBram Moolenaar /*
5372071d4279SBram Moolenaar * First, loop through the lists until one of them is empty.
5373071d4279SBram Moolenaar */
5374071d4279SBram Moolenaar while (*g1 && *g2)
5375071d4279SBram Moolenaar {
5376071d4279SBram Moolenaar /*
5377071d4279SBram Moolenaar * We always want to add from the first list.
5378071d4279SBram Moolenaar */
5379071d4279SBram Moolenaar if (*g1 < *g2)
5380071d4279SBram Moolenaar {
5381071d4279SBram Moolenaar if (round == 2)
5382071d4279SBram Moolenaar clstr[count] = *g1;
5383071d4279SBram Moolenaar count++;
5384071d4279SBram Moolenaar g1++;
5385071d4279SBram Moolenaar continue;
5386071d4279SBram Moolenaar }
5387071d4279SBram Moolenaar /*
5388071d4279SBram Moolenaar * We only want to add from the second list if we're adding the
5389071d4279SBram Moolenaar * lists.
5390071d4279SBram Moolenaar */
5391071d4279SBram Moolenaar if (list_op == CLUSTER_ADD)
5392071d4279SBram Moolenaar {
5393071d4279SBram Moolenaar if (round == 2)
5394071d4279SBram Moolenaar clstr[count] = *g2;
5395071d4279SBram Moolenaar count++;
5396071d4279SBram Moolenaar }
5397071d4279SBram Moolenaar if (*g1 == *g2)
5398071d4279SBram Moolenaar g1++;
5399071d4279SBram Moolenaar g2++;
5400071d4279SBram Moolenaar }
5401071d4279SBram Moolenaar
5402071d4279SBram Moolenaar /*
5403071d4279SBram Moolenaar * Now add the leftovers from whichever list didn't get finished
5404071d4279SBram Moolenaar * first. As before, we only want to add from the second list if
5405071d4279SBram Moolenaar * we're adding the lists.
5406071d4279SBram Moolenaar */
5407071d4279SBram Moolenaar for (; *g1; g1++, count++)
5408071d4279SBram Moolenaar if (round == 2)
5409071d4279SBram Moolenaar clstr[count] = *g1;
5410071d4279SBram Moolenaar if (list_op == CLUSTER_ADD)
5411071d4279SBram Moolenaar for (; *g2; g2++, count++)
5412071d4279SBram Moolenaar if (round == 2)
5413071d4279SBram Moolenaar clstr[count] = *g2;
5414071d4279SBram Moolenaar
5415071d4279SBram Moolenaar if (round == 1)
5416071d4279SBram Moolenaar {
5417071d4279SBram Moolenaar /*
5418071d4279SBram Moolenaar * If the group ended up empty, we don't need to allocate any
5419071d4279SBram Moolenaar * space for it.
5420071d4279SBram Moolenaar */
5421071d4279SBram Moolenaar if (count == 0)
5422071d4279SBram Moolenaar {
5423071d4279SBram Moolenaar clstr = NULL;
5424071d4279SBram Moolenaar break;
5425071d4279SBram Moolenaar }
5426c799fe20SBram Moolenaar clstr = ALLOC_MULT(short, count + 1);
5427071d4279SBram Moolenaar if (clstr == NULL)
5428071d4279SBram Moolenaar break;
5429071d4279SBram Moolenaar clstr[count] = 0;
5430071d4279SBram Moolenaar }
5431071d4279SBram Moolenaar }
5432071d4279SBram Moolenaar
5433071d4279SBram Moolenaar /*
5434071d4279SBram Moolenaar * Finally, put the new list in place.
5435071d4279SBram Moolenaar */
5436071d4279SBram Moolenaar vim_free(*clstr1);
5437071d4279SBram Moolenaar vim_free(*clstr2);
5438071d4279SBram Moolenaar *clstr1 = clstr;
5439071d4279SBram Moolenaar }
5440071d4279SBram Moolenaar
5441071d4279SBram Moolenaar /*
5442aab93b12SBram Moolenaar * Lookup a syntax cluster name and return its ID.
5443071d4279SBram Moolenaar * If it is not found, 0 is returned.
5444071d4279SBram Moolenaar */
5445071d4279SBram Moolenaar static int
syn_scl_name2id(char_u * name)5446764b23c8SBram Moolenaar syn_scl_name2id(char_u *name)
5447071d4279SBram Moolenaar {
5448071d4279SBram Moolenaar int i;
5449071d4279SBram Moolenaar char_u *name_u;
5450071d4279SBram Moolenaar
54510d6f5d97SBram Moolenaar // Avoid using stricmp() too much, it's slow on some systems
5452071d4279SBram Moolenaar name_u = vim_strsave_up(name);
5453071d4279SBram Moolenaar if (name_u == NULL)
5454071d4279SBram Moolenaar return 0;
5455860cae1cSBram Moolenaar for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5456860cae1cSBram Moolenaar if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5457860cae1cSBram Moolenaar && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
5458071d4279SBram Moolenaar break;
5459071d4279SBram Moolenaar vim_free(name_u);
5460071d4279SBram Moolenaar return (i < 0 ? 0 : i + SYNID_CLUSTER);
5461071d4279SBram Moolenaar }
5462071d4279SBram Moolenaar
5463071d4279SBram Moolenaar /*
5464071d4279SBram Moolenaar * Like syn_scl_name2id(), but take a pointer + length argument.
5465071d4279SBram Moolenaar */
5466071d4279SBram Moolenaar static int
syn_scl_namen2id(char_u * linep,int len)5467764b23c8SBram Moolenaar syn_scl_namen2id(char_u *linep, int len)
5468071d4279SBram Moolenaar {
5469071d4279SBram Moolenaar char_u *name;
5470071d4279SBram Moolenaar int id = 0;
5471071d4279SBram Moolenaar
5472071d4279SBram Moolenaar name = vim_strnsave(linep, len);
5473071d4279SBram Moolenaar if (name != NULL)
5474071d4279SBram Moolenaar {
5475071d4279SBram Moolenaar id = syn_scl_name2id(name);
5476071d4279SBram Moolenaar vim_free(name);
5477071d4279SBram Moolenaar }
5478071d4279SBram Moolenaar return id;
5479071d4279SBram Moolenaar }
5480071d4279SBram Moolenaar
5481071d4279SBram Moolenaar /*
5482aab93b12SBram Moolenaar * Find syntax cluster name in the table and return its ID.
5483071d4279SBram Moolenaar * The argument is a pointer to the name and the length of the name.
5484071d4279SBram Moolenaar * If it doesn't exist yet, a new entry is created.
5485071d4279SBram Moolenaar * Return 0 for failure.
5486071d4279SBram Moolenaar */
5487071d4279SBram Moolenaar static int
syn_check_cluster(char_u * pp,int len)5488764b23c8SBram Moolenaar syn_check_cluster(char_u *pp, int len)
5489071d4279SBram Moolenaar {
5490071d4279SBram Moolenaar int id;
5491071d4279SBram Moolenaar char_u *name;
5492071d4279SBram Moolenaar
5493071d4279SBram Moolenaar name = vim_strnsave(pp, len);
5494071d4279SBram Moolenaar if (name == NULL)
5495071d4279SBram Moolenaar return 0;
5496071d4279SBram Moolenaar
5497071d4279SBram Moolenaar id = syn_scl_name2id(name);
54980d6f5d97SBram Moolenaar if (id == 0) // doesn't exist yet
5499071d4279SBram Moolenaar id = syn_add_cluster(name);
5500071d4279SBram Moolenaar else
5501071d4279SBram Moolenaar vim_free(name);
5502071d4279SBram Moolenaar return id;
5503071d4279SBram Moolenaar }
5504071d4279SBram Moolenaar
5505071d4279SBram Moolenaar /*
5506aab93b12SBram Moolenaar * Add new syntax cluster and return its ID.
5507071d4279SBram Moolenaar * "name" must be an allocated string, it will be consumed.
5508071d4279SBram Moolenaar * Return 0 for failure.
5509071d4279SBram Moolenaar */
5510071d4279SBram Moolenaar static int
syn_add_cluster(char_u * name)5511764b23c8SBram Moolenaar syn_add_cluster(char_u *name)
5512071d4279SBram Moolenaar {
5513071d4279SBram Moolenaar int len;
5514071d4279SBram Moolenaar
5515071d4279SBram Moolenaar /*
5516071d4279SBram Moolenaar * First call for this growarray: init growing array.
5517071d4279SBram Moolenaar */
5518860cae1cSBram Moolenaar if (curwin->w_s->b_syn_clusters.ga_data == NULL)
5519071d4279SBram Moolenaar {
5520860cae1cSBram Moolenaar curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5521860cae1cSBram Moolenaar curwin->w_s->b_syn_clusters.ga_growsize = 10;
5522071d4279SBram Moolenaar }
5523071d4279SBram Moolenaar
552442431a7aSBram Moolenaar len = curwin->w_s->b_syn_clusters.ga_len;
552542431a7aSBram Moolenaar if (len >= MAX_CLUSTER_ID)
552642431a7aSBram Moolenaar {
5527f9e3e09fSBram Moolenaar emsg(_("E848: Too many syntax clusters"));
552842431a7aSBram Moolenaar vim_free(name);
552942431a7aSBram Moolenaar return 0;
553042431a7aSBram Moolenaar }
553142431a7aSBram Moolenaar
5532071d4279SBram Moolenaar /*
5533071d4279SBram Moolenaar * Make room for at least one other cluster entry.
5534071d4279SBram Moolenaar */
5535860cae1cSBram Moolenaar if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
5536071d4279SBram Moolenaar {
5537071d4279SBram Moolenaar vim_free(name);
5538071d4279SBram Moolenaar return 0;
5539071d4279SBram Moolenaar }
5540071d4279SBram Moolenaar
5541a80faa89SBram Moolenaar CLEAR_POINTER(&(SYN_CLSTR(curwin->w_s)[len]));
5542860cae1cSBram Moolenaar SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5543860cae1cSBram Moolenaar SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5544860cae1cSBram Moolenaar SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5545860cae1cSBram Moolenaar ++curwin->w_s->b_syn_clusters.ga_len;
5546071d4279SBram Moolenaar
5547217ad920SBram Moolenaar if (STRICMP(name, "Spell") == 0)
5548860cae1cSBram Moolenaar curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
55496bb68366SBram Moolenaar if (STRICMP(name, "NoSpell") == 0)
5550860cae1cSBram Moolenaar curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
5551217ad920SBram Moolenaar
5552071d4279SBram Moolenaar return len + SYNID_CLUSTER;
5553071d4279SBram Moolenaar }
5554071d4279SBram Moolenaar
5555071d4279SBram Moolenaar /*
5556071d4279SBram Moolenaar * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5557071d4279SBram Moolenaar * [add={groupname},..] [remove={groupname},..]".
5558071d4279SBram Moolenaar */
5559071d4279SBram Moolenaar static void
syn_cmd_cluster(exarg_T * eap,int syncing UNUSED)5560764b23c8SBram Moolenaar syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
5561071d4279SBram Moolenaar {
5562071d4279SBram Moolenaar char_u *arg = eap->arg;
5563071d4279SBram Moolenaar char_u *group_name_end;
5564071d4279SBram Moolenaar char_u *rest;
5565071d4279SBram Moolenaar int scl_id;
5566071d4279SBram Moolenaar short *clstr_list;
5567071d4279SBram Moolenaar int got_clstr = FALSE;
5568071d4279SBram Moolenaar int opt_len;
5569071d4279SBram Moolenaar int list_op;
5570071d4279SBram Moolenaar
5571071d4279SBram Moolenaar eap->nextcmd = find_nextcmd(arg);
5572071d4279SBram Moolenaar if (eap->skip)
5573071d4279SBram Moolenaar return;
5574071d4279SBram Moolenaar
5575071d4279SBram Moolenaar rest = get_group_name(arg, &group_name_end);
5576071d4279SBram Moolenaar
5577071d4279SBram Moolenaar if (rest != NULL)
5578071d4279SBram Moolenaar {
557942431a7aSBram Moolenaar scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
558042431a7aSBram Moolenaar if (scl_id == 0)
558142431a7aSBram Moolenaar return;
558242431a7aSBram Moolenaar scl_id -= SYNID_CLUSTER;
5583071d4279SBram Moolenaar
5584071d4279SBram Moolenaar for (;;)
5585071d4279SBram Moolenaar {
5586071d4279SBram Moolenaar if (STRNICMP(rest, "add", 3) == 0
55871c465444SBram Moolenaar && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
5588071d4279SBram Moolenaar {
5589071d4279SBram Moolenaar opt_len = 3;
5590071d4279SBram Moolenaar list_op = CLUSTER_ADD;
5591071d4279SBram Moolenaar }
5592071d4279SBram Moolenaar else if (STRNICMP(rest, "remove", 6) == 0
55931c465444SBram Moolenaar && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
5594071d4279SBram Moolenaar {
5595071d4279SBram Moolenaar opt_len = 6;
5596071d4279SBram Moolenaar list_op = CLUSTER_SUBTRACT;
5597071d4279SBram Moolenaar }
5598071d4279SBram Moolenaar else if (STRNICMP(rest, "contains", 8) == 0
55991c465444SBram Moolenaar && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
5600071d4279SBram Moolenaar {
5601071d4279SBram Moolenaar opt_len = 8;
5602071d4279SBram Moolenaar list_op = CLUSTER_REPLACE;
5603071d4279SBram Moolenaar }
5604071d4279SBram Moolenaar else
5605071d4279SBram Moolenaar break;
5606071d4279SBram Moolenaar
5607071d4279SBram Moolenaar clstr_list = NULL;
5608de318c5cSBram Moolenaar if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
5609071d4279SBram Moolenaar {
5610f9e3e09fSBram Moolenaar semsg(_(e_invarg2), rest);
5611071d4279SBram Moolenaar break;
5612071d4279SBram Moolenaar }
5613de318c5cSBram Moolenaar if (scl_id >= 0)
5614860cae1cSBram Moolenaar syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
5615071d4279SBram Moolenaar &clstr_list, list_op);
5616d7a96151SBram Moolenaar else
5617d7a96151SBram Moolenaar vim_free(clstr_list);
5618071d4279SBram Moolenaar got_clstr = TRUE;
5619071d4279SBram Moolenaar }
5620071d4279SBram Moolenaar
5621071d4279SBram Moolenaar if (got_clstr)
5622071d4279SBram Moolenaar {
56231c8f93ffSBram Moolenaar redraw_curbuf_later(SOME_VALID);
56240d6f5d97SBram Moolenaar syn_stack_free_all(curwin->w_s); // Need to recompute all.
5625071d4279SBram Moolenaar }
5626071d4279SBram Moolenaar }
5627071d4279SBram Moolenaar
5628071d4279SBram Moolenaar if (!got_clstr)
5629f9e3e09fSBram Moolenaar emsg(_("E400: No cluster specified"));
56301966c248SBram Moolenaar if (rest == NULL || !ends_excmd2(eap->cmd, rest))
5631f9e3e09fSBram Moolenaar semsg(_(e_invarg2), arg);
5632071d4279SBram Moolenaar }
5633071d4279SBram Moolenaar
5634071d4279SBram Moolenaar /*
5635071d4279SBram Moolenaar * On first call for current buffer: Init growing array.
5636071d4279SBram Moolenaar */
5637071d4279SBram Moolenaar static void
init_syn_patterns(void)5638764b23c8SBram Moolenaar init_syn_patterns(void)
5639071d4279SBram Moolenaar {
5640860cae1cSBram Moolenaar curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5641860cae1cSBram Moolenaar curwin->w_s->b_syn_patterns.ga_growsize = 10;
5642071d4279SBram Moolenaar }
5643071d4279SBram Moolenaar
5644071d4279SBram Moolenaar /*
5645071d4279SBram Moolenaar * Get one pattern for a ":syntax match" or ":syntax region" command.
5646071d4279SBram Moolenaar * Stores the pattern and program in a synpat_T.
5647071d4279SBram Moolenaar * Returns a pointer to the next argument, or NULL in case of an error.
5648071d4279SBram Moolenaar */
5649071d4279SBram Moolenaar static char_u *
get_syn_pattern(char_u * arg,synpat_T * ci)5650764b23c8SBram Moolenaar get_syn_pattern(char_u *arg, synpat_T *ci)
5651071d4279SBram Moolenaar {
5652071d4279SBram Moolenaar char_u *end;
5653071d4279SBram Moolenaar int *p;
5654071d4279SBram Moolenaar int idx;
5655071d4279SBram Moolenaar char_u *cpo_save;
5656071d4279SBram Moolenaar
56570d6f5d97SBram Moolenaar // need at least three chars
565838219786SBram Moolenaar if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
5659071d4279SBram Moolenaar return NULL;
5660071d4279SBram Moolenaar
5661e8c4abbbSBram Moolenaar end = skip_regexp(arg + 1, *arg, TRUE);
56620d6f5d97SBram Moolenaar if (*end != *arg) // end delimiter not found
5663071d4279SBram Moolenaar {
5664f9e3e09fSBram Moolenaar semsg(_("E401: Pattern delimiter not found: %s"), arg);
5665071d4279SBram Moolenaar return NULL;
5666071d4279SBram Moolenaar }
56670d6f5d97SBram Moolenaar // store the pattern and compiled regexp program
566871ccd03eSBram Moolenaar if ((ci->sp_pattern = vim_strnsave(arg + 1, end - arg - 1)) == NULL)
5669071d4279SBram Moolenaar return NULL;
5670071d4279SBram Moolenaar
56710d6f5d97SBram Moolenaar // Make 'cpoptions' empty, to avoid the 'l' flag
5672071d4279SBram Moolenaar cpo_save = p_cpo;
5673e5a2dc87SBram Moolenaar p_cpo = empty_option;
5674071d4279SBram Moolenaar ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5675071d4279SBram Moolenaar p_cpo = cpo_save;
5676071d4279SBram Moolenaar
5677071d4279SBram Moolenaar if (ci->sp_prog == NULL)
5678071d4279SBram Moolenaar return NULL;
5679860cae1cSBram Moolenaar ci->sp_ic = curwin->w_s->b_syn_ic;
5680f7512552SBram Moolenaar #ifdef FEAT_PROFILE
56818a7f5a2dSBram Moolenaar syn_clear_time(&ci->sp_time);
56828a7f5a2dSBram Moolenaar #endif
5683071d4279SBram Moolenaar
5684071d4279SBram Moolenaar /*
5685071d4279SBram Moolenaar * Check for a match, highlight or region offset.
5686071d4279SBram Moolenaar */
5687071d4279SBram Moolenaar ++end;
5688071d4279SBram Moolenaar do
5689071d4279SBram Moolenaar {
5690071d4279SBram Moolenaar for (idx = SPO_COUNT; --idx >= 0; )
5691071d4279SBram Moolenaar if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5692071d4279SBram Moolenaar break;
5693071d4279SBram Moolenaar if (idx >= 0)
5694071d4279SBram Moolenaar {
5695071d4279SBram Moolenaar p = &(ci->sp_offsets[idx]);
5696071d4279SBram Moolenaar if (idx != SPO_LC_OFF)
5697071d4279SBram Moolenaar switch (end[3])
5698071d4279SBram Moolenaar {
5699071d4279SBram Moolenaar case 's': break;
5700071d4279SBram Moolenaar case 'b': break;
5701071d4279SBram Moolenaar case 'e': idx += SPO_COUNT; break;
5702071d4279SBram Moolenaar default: idx = -1; break;
5703071d4279SBram Moolenaar }
5704071d4279SBram Moolenaar if (idx >= 0)
5705071d4279SBram Moolenaar {
5706071d4279SBram Moolenaar ci->sp_off_flags |= (1 << idx);
57070d6f5d97SBram Moolenaar if (idx == SPO_LC_OFF) // lc=99
5708071d4279SBram Moolenaar {
5709071d4279SBram Moolenaar end += 3;
5710071d4279SBram Moolenaar *p = getdigits(&end);
5711071d4279SBram Moolenaar
57120d6f5d97SBram Moolenaar // "lc=" offset automatically sets "ms=" offset
5713071d4279SBram Moolenaar if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5714071d4279SBram Moolenaar {
5715071d4279SBram Moolenaar ci->sp_off_flags |= (1 << SPO_MS_OFF);
5716071d4279SBram Moolenaar ci->sp_offsets[SPO_MS_OFF] = *p;
5717071d4279SBram Moolenaar }
5718071d4279SBram Moolenaar }
57190d6f5d97SBram Moolenaar else // yy=x+99
5720071d4279SBram Moolenaar {
5721071d4279SBram Moolenaar end += 4;
5722071d4279SBram Moolenaar if (*end == '+')
5723071d4279SBram Moolenaar {
5724071d4279SBram Moolenaar ++end;
57250d6f5d97SBram Moolenaar *p = getdigits(&end); // positive offset
5726071d4279SBram Moolenaar }
5727071d4279SBram Moolenaar else if (*end == '-')
5728071d4279SBram Moolenaar {
5729071d4279SBram Moolenaar ++end;
57300d6f5d97SBram Moolenaar *p = -getdigits(&end); // negative offset
5731071d4279SBram Moolenaar }
5732071d4279SBram Moolenaar }
5733071d4279SBram Moolenaar if (*end != ',')
5734071d4279SBram Moolenaar break;
5735071d4279SBram Moolenaar ++end;
5736071d4279SBram Moolenaar }
5737071d4279SBram Moolenaar }
5738071d4279SBram Moolenaar } while (idx >= 0);
5739071d4279SBram Moolenaar
57401966c248SBram Moolenaar if (!ends_excmd2(arg, end) && !VIM_ISWHITE(*end))
5741071d4279SBram Moolenaar {
5742f9e3e09fSBram Moolenaar semsg(_("E402: Garbage after pattern: %s"), arg);
5743071d4279SBram Moolenaar return NULL;
5744071d4279SBram Moolenaar }
5745071d4279SBram Moolenaar return skipwhite(end);
5746071d4279SBram Moolenaar }
5747071d4279SBram Moolenaar
5748071d4279SBram Moolenaar /*
5749071d4279SBram Moolenaar * Handle ":syntax sync .." command.
5750071d4279SBram Moolenaar */
5751071d4279SBram Moolenaar static void
syn_cmd_sync(exarg_T * eap,int syncing UNUSED)5752764b23c8SBram Moolenaar syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
5753071d4279SBram Moolenaar {
5754071d4279SBram Moolenaar char_u *arg_start = eap->arg;
5755071d4279SBram Moolenaar char_u *arg_end;
5756071d4279SBram Moolenaar char_u *key = NULL;
5757071d4279SBram Moolenaar char_u *next_arg;
5758071d4279SBram Moolenaar int illegal = FALSE;
5759071d4279SBram Moolenaar int finished = FALSE;
5760071d4279SBram Moolenaar long n;
5761071d4279SBram Moolenaar char_u *cpo_save;
5762071d4279SBram Moolenaar
57631966c248SBram Moolenaar if (ends_excmd2(eap->cmd, arg_start))
5764071d4279SBram Moolenaar {
5765071d4279SBram Moolenaar syn_cmd_list(eap, TRUE);
5766071d4279SBram Moolenaar return;
5767071d4279SBram Moolenaar }
5768071d4279SBram Moolenaar
57691966c248SBram Moolenaar while (!ends_excmd2(eap->cmd, arg_start))
5770071d4279SBram Moolenaar {
5771071d4279SBram Moolenaar arg_end = skiptowhite(arg_start);
5772071d4279SBram Moolenaar next_arg = skipwhite(arg_end);
5773071d4279SBram Moolenaar vim_free(key);
5774df44a27bSBram Moolenaar key = vim_strnsave_up(arg_start, arg_end - arg_start);
577558bb61cfSBram Moolenaar if (key == NULL)
577658bb61cfSBram Moolenaar break;
5777071d4279SBram Moolenaar if (STRCMP(key, "CCOMMENT") == 0)
5778071d4279SBram Moolenaar {
5779071d4279SBram Moolenaar if (!eap->skip)
5780860cae1cSBram Moolenaar curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
57811966c248SBram Moolenaar if (!ends_excmd2(eap->cmd, next_arg))
5782071d4279SBram Moolenaar {
5783071d4279SBram Moolenaar arg_end = skiptowhite(next_arg);
5784071d4279SBram Moolenaar if (!eap->skip)
5785860cae1cSBram Moolenaar curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
5786071d4279SBram Moolenaar (int)(arg_end - next_arg));
5787071d4279SBram Moolenaar next_arg = skipwhite(arg_end);
5788071d4279SBram Moolenaar }
5789071d4279SBram Moolenaar else if (!eap->skip)
5790860cae1cSBram Moolenaar curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
5791071d4279SBram Moolenaar }
5792071d4279SBram Moolenaar else if ( STRNCMP(key, "LINES", 5) == 0
5793071d4279SBram Moolenaar || STRNCMP(key, "MINLINES", 8) == 0
5794071d4279SBram Moolenaar || STRNCMP(key, "MAXLINES", 8) == 0
5795071d4279SBram Moolenaar || STRNCMP(key, "LINEBREAKS", 10) == 0)
5796071d4279SBram Moolenaar {
5797071d4279SBram Moolenaar if (key[4] == 'S')
5798071d4279SBram Moolenaar arg_end = key + 6;
5799071d4279SBram Moolenaar else if (key[0] == 'L')
5800071d4279SBram Moolenaar arg_end = key + 11;
5801071d4279SBram Moolenaar else
5802071d4279SBram Moolenaar arg_end = key + 9;
5803071d4279SBram Moolenaar if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5804071d4279SBram Moolenaar {
5805071d4279SBram Moolenaar illegal = TRUE;
5806071d4279SBram Moolenaar break;
5807071d4279SBram Moolenaar }
5808071d4279SBram Moolenaar n = getdigits(&arg_end);
5809071d4279SBram Moolenaar if (!eap->skip)
5810071d4279SBram Moolenaar {
5811071d4279SBram Moolenaar if (key[4] == 'B')
5812860cae1cSBram Moolenaar curwin->w_s->b_syn_sync_linebreaks = n;
5813071d4279SBram Moolenaar else if (key[1] == 'A')
5814860cae1cSBram Moolenaar curwin->w_s->b_syn_sync_maxlines = n;
5815071d4279SBram Moolenaar else
5816860cae1cSBram Moolenaar curwin->w_s->b_syn_sync_minlines = n;
5817071d4279SBram Moolenaar }
5818071d4279SBram Moolenaar }
5819071d4279SBram Moolenaar else if (STRCMP(key, "FROMSTART") == 0)
5820071d4279SBram Moolenaar {
5821071d4279SBram Moolenaar if (!eap->skip)
5822071d4279SBram Moolenaar {
5823860cae1cSBram Moolenaar curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5824860cae1cSBram Moolenaar curwin->w_s->b_syn_sync_maxlines = 0;
5825071d4279SBram Moolenaar }
5826071d4279SBram Moolenaar }
5827071d4279SBram Moolenaar else if (STRCMP(key, "LINECONT") == 0)
5828071d4279SBram Moolenaar {
58290d6f5d97SBram Moolenaar if (*next_arg == NUL) // missing pattern
58302795e21eSBram Moolenaar {
58312795e21eSBram Moolenaar illegal = TRUE;
58322795e21eSBram Moolenaar break;
58332795e21eSBram Moolenaar }
5834860cae1cSBram Moolenaar if (curwin->w_s->b_syn_linecont_pat != NULL)
5835071d4279SBram Moolenaar {
5836f9e3e09fSBram Moolenaar emsg(_("E403: syntax sync: line continuations pattern specified twice"));
5837071d4279SBram Moolenaar finished = TRUE;
5838071d4279SBram Moolenaar break;
5839071d4279SBram Moolenaar }
5840e8c4abbbSBram Moolenaar arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE);
58410d6f5d97SBram Moolenaar if (*arg_end != *next_arg) // end delimiter not found
5842071d4279SBram Moolenaar {
5843071d4279SBram Moolenaar illegal = TRUE;
5844071d4279SBram Moolenaar break;
5845071d4279SBram Moolenaar }
5846071d4279SBram Moolenaar
5847071d4279SBram Moolenaar if (!eap->skip)
5848071d4279SBram Moolenaar {
58490d6f5d97SBram Moolenaar // store the pattern and compiled regexp program
585071ccd03eSBram Moolenaar if ((curwin->w_s->b_syn_linecont_pat =
585171ccd03eSBram Moolenaar vim_strnsave(next_arg + 1,
585271ccd03eSBram Moolenaar arg_end - next_arg - 1)) == NULL)
5853071d4279SBram Moolenaar {
5854071d4279SBram Moolenaar finished = TRUE;
5855071d4279SBram Moolenaar break;
5856071d4279SBram Moolenaar }
5857860cae1cSBram Moolenaar curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
5858071d4279SBram Moolenaar
58590d6f5d97SBram Moolenaar // Make 'cpoptions' empty, to avoid the 'l' flag
5860071d4279SBram Moolenaar cpo_save = p_cpo;
5861e5a2dc87SBram Moolenaar p_cpo = empty_option;
5862860cae1cSBram Moolenaar curwin->w_s->b_syn_linecont_prog =
5863860cae1cSBram Moolenaar vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
5864071d4279SBram Moolenaar p_cpo = cpo_save;
5865f7512552SBram Moolenaar #ifdef FEAT_PROFILE
58668a7f5a2dSBram Moolenaar syn_clear_time(&curwin->w_s->b_syn_linecont_time);
58678a7f5a2dSBram Moolenaar #endif
5868071d4279SBram Moolenaar
5869860cae1cSBram Moolenaar if (curwin->w_s->b_syn_linecont_prog == NULL)
5870071d4279SBram Moolenaar {
5871d23a8236SBram Moolenaar VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
5872071d4279SBram Moolenaar finished = TRUE;
5873071d4279SBram Moolenaar break;
5874071d4279SBram Moolenaar }
5875071d4279SBram Moolenaar }
5876071d4279SBram Moolenaar next_arg = skipwhite(arg_end + 1);
5877071d4279SBram Moolenaar }
5878071d4279SBram Moolenaar else
5879071d4279SBram Moolenaar {
5880071d4279SBram Moolenaar eap->arg = next_arg;
5881071d4279SBram Moolenaar if (STRCMP(key, "MATCH") == 0)
5882071d4279SBram Moolenaar syn_cmd_match(eap, TRUE);
5883071d4279SBram Moolenaar else if (STRCMP(key, "REGION") == 0)
5884071d4279SBram Moolenaar syn_cmd_region(eap, TRUE);
5885071d4279SBram Moolenaar else if (STRCMP(key, "CLEAR") == 0)
5886071d4279SBram Moolenaar syn_cmd_clear(eap, TRUE);
5887071d4279SBram Moolenaar else
5888071d4279SBram Moolenaar illegal = TRUE;
5889071d4279SBram Moolenaar finished = TRUE;
5890071d4279SBram Moolenaar break;
5891071d4279SBram Moolenaar }
5892071d4279SBram Moolenaar arg_start = next_arg;
5893071d4279SBram Moolenaar }
5894071d4279SBram Moolenaar vim_free(key);
5895071d4279SBram Moolenaar if (illegal)
5896f9e3e09fSBram Moolenaar semsg(_("E404: Illegal arguments: %s"), arg_start);
5897071d4279SBram Moolenaar else if (!finished)
5898071d4279SBram Moolenaar {
589963b91736SBram Moolenaar set_nextcmd(eap, arg_start);
59001c8f93ffSBram Moolenaar redraw_curbuf_later(SOME_VALID);
59010d6f5d97SBram Moolenaar syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
5902071d4279SBram Moolenaar }
5903071d4279SBram Moolenaar }
5904071d4279SBram Moolenaar
5905071d4279SBram Moolenaar /*
5906071d4279SBram Moolenaar * Convert a line of highlight group names into a list of group ID numbers.
5907071d4279SBram Moolenaar * "arg" should point to the "contains" or "nextgroup" keyword.
5908071d4279SBram Moolenaar * "arg" is advanced to after the last group name.
5909071d4279SBram Moolenaar * Careful: the argument is modified (NULs added).
5910071d4279SBram Moolenaar * returns FAIL for some error, OK for success.
5911071d4279SBram Moolenaar */
5912071d4279SBram Moolenaar static int
get_id_list(char_u ** arg,int keylen,short ** list,int skip)5913764b23c8SBram Moolenaar get_id_list(
5914764b23c8SBram Moolenaar char_u **arg,
59150d6f5d97SBram Moolenaar int keylen, // length of keyword
59160d6f5d97SBram Moolenaar short **list, // where to store the resulting list, if not
59170d6f5d97SBram Moolenaar // NULL, the list is silently skipped!
5918de318c5cSBram Moolenaar int skip)
5919071d4279SBram Moolenaar {
5920071d4279SBram Moolenaar char_u *p = NULL;
5921071d4279SBram Moolenaar char_u *end;
5922071d4279SBram Moolenaar int round;
5923071d4279SBram Moolenaar int count;
5924071d4279SBram Moolenaar int total_count = 0;
5925071d4279SBram Moolenaar short *retval = NULL;
5926071d4279SBram Moolenaar char_u *name;
5927071d4279SBram Moolenaar regmatch_T regmatch;
5928071d4279SBram Moolenaar int id;
5929071d4279SBram Moolenaar int i;
5930071d4279SBram Moolenaar int failed = FALSE;
5931071d4279SBram Moolenaar
5932071d4279SBram Moolenaar /*
5933071d4279SBram Moolenaar * We parse the list twice:
5934071d4279SBram Moolenaar * round == 1: count the number of items, allocate the array.
5935071d4279SBram Moolenaar * round == 2: fill the array with the items.
5936071d4279SBram Moolenaar * In round 1 new groups may be added, causing the number of items to
5937071d4279SBram Moolenaar * grow when a regexp is used. In that case round 1 is done once again.
5938071d4279SBram Moolenaar */
5939071d4279SBram Moolenaar for (round = 1; round <= 2; ++round)
5940071d4279SBram Moolenaar {
5941071d4279SBram Moolenaar /*
5942071d4279SBram Moolenaar * skip "contains"
5943071d4279SBram Moolenaar */
5944071d4279SBram Moolenaar p = skipwhite(*arg + keylen);
5945071d4279SBram Moolenaar if (*p != '=')
5946071d4279SBram Moolenaar {
5947f9e3e09fSBram Moolenaar semsg(_("E405: Missing equal sign: %s"), *arg);
5948071d4279SBram Moolenaar break;
5949071d4279SBram Moolenaar }
5950071d4279SBram Moolenaar p = skipwhite(p + 1);
59511966c248SBram Moolenaar if (ends_excmd2(*arg, p))
5952071d4279SBram Moolenaar {
5953f9e3e09fSBram Moolenaar semsg(_("E406: Empty argument: %s"), *arg);
5954071d4279SBram Moolenaar break;
5955071d4279SBram Moolenaar }
5956071d4279SBram Moolenaar
5957071d4279SBram Moolenaar /*
5958071d4279SBram Moolenaar * parse the arguments after "contains"
5959071d4279SBram Moolenaar */
5960071d4279SBram Moolenaar count = 0;
59611966c248SBram Moolenaar while (!ends_excmd2(*arg, p))
5962071d4279SBram Moolenaar {
59631c465444SBram Moolenaar for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
5964071d4279SBram Moolenaar ;
59650d6f5d97SBram Moolenaar name = alloc(end - p + 3); // leave room for "^$"
5966071d4279SBram Moolenaar if (name == NULL)
5967071d4279SBram Moolenaar {
5968071d4279SBram Moolenaar failed = TRUE;
5969071d4279SBram Moolenaar break;
5970071d4279SBram Moolenaar }
5971ce0842a6SBram Moolenaar vim_strncpy(name + 1, p, end - p);
5972071d4279SBram Moolenaar if ( STRCMP(name + 1, "ALLBUT") == 0
5973071d4279SBram Moolenaar || STRCMP(name + 1, "ALL") == 0
5974071d4279SBram Moolenaar || STRCMP(name + 1, "TOP") == 0
5975071d4279SBram Moolenaar || STRCMP(name + 1, "CONTAINED") == 0)
5976071d4279SBram Moolenaar {
5977071d4279SBram Moolenaar if (TOUPPER_ASC(**arg) != 'C')
5978071d4279SBram Moolenaar {
5979f9e3e09fSBram Moolenaar semsg(_("E407: %s not allowed here"), name + 1);
5980071d4279SBram Moolenaar failed = TRUE;
5981071d4279SBram Moolenaar vim_free(name);
5982071d4279SBram Moolenaar break;
5983071d4279SBram Moolenaar }
5984071d4279SBram Moolenaar if (count != 0)
5985071d4279SBram Moolenaar {
5986f9e3e09fSBram Moolenaar semsg(_("E408: %s must be first in contains list"),
5987d7a96151SBram Moolenaar name + 1);
5988071d4279SBram Moolenaar failed = TRUE;
5989071d4279SBram Moolenaar vim_free(name);
5990071d4279SBram Moolenaar break;
5991071d4279SBram Moolenaar }
5992071d4279SBram Moolenaar if (name[1] == 'A')
59932e240bd4SBram Moolenaar id = SYNID_ALLBUT + current_syn_inc_tag;
5994071d4279SBram Moolenaar else if (name[1] == 'T')
59952e240bd4SBram Moolenaar {
59962e240bd4SBram Moolenaar if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
59972e240bd4SBram Moolenaar id = curwin->w_s->b_syn_topgrp;
5998071d4279SBram Moolenaar else
59992e240bd4SBram Moolenaar id = SYNID_TOP + current_syn_inc_tag;
60002e240bd4SBram Moolenaar }
60012e240bd4SBram Moolenaar else
60022e240bd4SBram Moolenaar id = SYNID_CONTAINED + current_syn_inc_tag;
60032e240bd4SBram Moolenaar
6004071d4279SBram Moolenaar }
6005071d4279SBram Moolenaar else if (name[1] == '@')
6006071d4279SBram Moolenaar {
6007eb46f8faSBram Moolenaar if (skip)
6008eb46f8faSBram Moolenaar id = -1;
6009eb46f8faSBram Moolenaar else
6010071d4279SBram Moolenaar id = syn_check_cluster(name + 2, (int)(end - p - 1));
6011071d4279SBram Moolenaar }
6012071d4279SBram Moolenaar else
6013071d4279SBram Moolenaar {
6014071d4279SBram Moolenaar /*
6015071d4279SBram Moolenaar * Handle full group name.
6016071d4279SBram Moolenaar */
6017071d4279SBram Moolenaar if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6018071d4279SBram Moolenaar id = syn_check_group(name + 1, (int)(end - p));
6019071d4279SBram Moolenaar else
6020071d4279SBram Moolenaar {
6021071d4279SBram Moolenaar /*
6022071d4279SBram Moolenaar * Handle match of regexp with group names.
6023071d4279SBram Moolenaar */
6024071d4279SBram Moolenaar *name = '^';
6025071d4279SBram Moolenaar STRCAT(name, "$");
6026071d4279SBram Moolenaar regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6027071d4279SBram Moolenaar if (regmatch.regprog == NULL)
6028071d4279SBram Moolenaar {
6029071d4279SBram Moolenaar failed = TRUE;
6030071d4279SBram Moolenaar vim_free(name);
6031071d4279SBram Moolenaar break;
6032071d4279SBram Moolenaar }
6033071d4279SBram Moolenaar
6034071d4279SBram Moolenaar regmatch.rm_ic = TRUE;
6035071d4279SBram Moolenaar id = 0;
60362ac6e82aSBram Moolenaar for (i = highlight_num_groups(); --i >= 0; )
6037071d4279SBram Moolenaar {
60382ac6e82aSBram Moolenaar if (vim_regexec(®match, highlight_group_name(i),
6039071d4279SBram Moolenaar (colnr_T)0))
6040071d4279SBram Moolenaar {
6041071d4279SBram Moolenaar if (round == 2)
6042071d4279SBram Moolenaar {
60430d6f5d97SBram Moolenaar // Got more items than expected; can happen
60440d6f5d97SBram Moolenaar // when adding items that match:
60450d6f5d97SBram Moolenaar // "contains=a.*b,axb".
60460d6f5d97SBram Moolenaar // Go back to first round
6047071d4279SBram Moolenaar if (count >= total_count)
6048071d4279SBram Moolenaar {
6049071d4279SBram Moolenaar vim_free(retval);
6050071d4279SBram Moolenaar round = 1;
6051071d4279SBram Moolenaar }
6052071d4279SBram Moolenaar else
6053071d4279SBram Moolenaar retval[count] = i + 1;
6054071d4279SBram Moolenaar }
6055071d4279SBram Moolenaar ++count;
60560d6f5d97SBram Moolenaar id = -1; // remember that we found one
6057071d4279SBram Moolenaar }
6058071d4279SBram Moolenaar }
6059473de61bSBram Moolenaar vim_regfree(regmatch.regprog);
6060071d4279SBram Moolenaar }
6061071d4279SBram Moolenaar }
6062071d4279SBram Moolenaar vim_free(name);
6063071d4279SBram Moolenaar if (id == 0)
6064071d4279SBram Moolenaar {
6065f9e3e09fSBram Moolenaar semsg(_("E409: Unknown group name: %s"), p);
6066071d4279SBram Moolenaar failed = TRUE;
6067071d4279SBram Moolenaar break;
6068071d4279SBram Moolenaar }
6069071d4279SBram Moolenaar if (id > 0)
6070071d4279SBram Moolenaar {
6071071d4279SBram Moolenaar if (round == 2)
6072071d4279SBram Moolenaar {
60730d6f5d97SBram Moolenaar // Got more items than expected, go back to first round
6074071d4279SBram Moolenaar if (count >= total_count)
6075071d4279SBram Moolenaar {
6076071d4279SBram Moolenaar vim_free(retval);
6077071d4279SBram Moolenaar round = 1;
6078071d4279SBram Moolenaar }
6079071d4279SBram Moolenaar else
6080071d4279SBram Moolenaar retval[count] = id;
6081071d4279SBram Moolenaar }
6082071d4279SBram Moolenaar ++count;
6083071d4279SBram Moolenaar }
6084071d4279SBram Moolenaar p = skipwhite(end);
6085071d4279SBram Moolenaar if (*p != ',')
6086071d4279SBram Moolenaar break;
60870d6f5d97SBram Moolenaar p = skipwhite(p + 1); // skip comma in between arguments
6088071d4279SBram Moolenaar }
6089071d4279SBram Moolenaar if (failed)
6090071d4279SBram Moolenaar break;
6091071d4279SBram Moolenaar if (round == 1)
6092071d4279SBram Moolenaar {
6093c799fe20SBram Moolenaar retval = ALLOC_MULT(short, count + 1);
6094071d4279SBram Moolenaar if (retval == NULL)
6095071d4279SBram Moolenaar break;
60960d6f5d97SBram Moolenaar retval[count] = 0; // zero means end of the list
6097071d4279SBram Moolenaar total_count = count;
6098071d4279SBram Moolenaar }
6099071d4279SBram Moolenaar }
6100071d4279SBram Moolenaar
6101071d4279SBram Moolenaar *arg = p;
6102071d4279SBram Moolenaar if (failed || retval == NULL)
6103071d4279SBram Moolenaar {
6104071d4279SBram Moolenaar vim_free(retval);
6105071d4279SBram Moolenaar return FAIL;
6106071d4279SBram Moolenaar }
6107071d4279SBram Moolenaar
6108071d4279SBram Moolenaar if (*list == NULL)
6109071d4279SBram Moolenaar *list = retval;
6110071d4279SBram Moolenaar else
61110d6f5d97SBram Moolenaar vim_free(retval); // list already found, don't overwrite it
6112071d4279SBram Moolenaar
6113071d4279SBram Moolenaar return OK;
6114071d4279SBram Moolenaar }
6115071d4279SBram Moolenaar
6116071d4279SBram Moolenaar /*
6117071d4279SBram Moolenaar * Make a copy of an ID list.
6118071d4279SBram Moolenaar */
6119071d4279SBram Moolenaar static short *
copy_id_list(short * list)6120764b23c8SBram Moolenaar copy_id_list(short *list)
6121071d4279SBram Moolenaar {
6122071d4279SBram Moolenaar int len;
6123071d4279SBram Moolenaar int count;
6124071d4279SBram Moolenaar short *retval;
6125071d4279SBram Moolenaar
6126071d4279SBram Moolenaar if (list == NULL)
6127071d4279SBram Moolenaar return NULL;
6128071d4279SBram Moolenaar
6129071d4279SBram Moolenaar for (count = 0; list[count]; ++count)
6130071d4279SBram Moolenaar ;
6131071d4279SBram Moolenaar len = (count + 1) * sizeof(short);
6132c799fe20SBram Moolenaar retval = alloc(len);
6133071d4279SBram Moolenaar if (retval != NULL)
6134071d4279SBram Moolenaar mch_memmove(retval, list, (size_t)len);
6135071d4279SBram Moolenaar
6136071d4279SBram Moolenaar return retval;
6137071d4279SBram Moolenaar }
6138071d4279SBram Moolenaar
6139071d4279SBram Moolenaar /*
6140071d4279SBram Moolenaar * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6141071d4279SBram Moolenaar * "cur_si" can be NULL if not checking the "containedin" list.
6142071d4279SBram Moolenaar * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6143071d4279SBram Moolenaar * the current item.
6144071d4279SBram Moolenaar * This function is called very often, keep it fast!!
6145071d4279SBram Moolenaar */
6146071d4279SBram Moolenaar static int
in_id_list(stateitem_T * cur_si,short * list,struct sp_syn * ssp,int contained)6147764b23c8SBram Moolenaar in_id_list(
61480d6f5d97SBram Moolenaar stateitem_T *cur_si, // current item or NULL
61490d6f5d97SBram Moolenaar short *list, // id list
61500d6f5d97SBram Moolenaar struct sp_syn *ssp, // group id and ":syn include" tag of group
61510d6f5d97SBram Moolenaar int contained) // group id is contained
6152071d4279SBram Moolenaar {
6153071d4279SBram Moolenaar int retval;
6154071d4279SBram Moolenaar short *scl_list;
6155071d4279SBram Moolenaar short item;
6156071d4279SBram Moolenaar short id = ssp->id;
6157071d4279SBram Moolenaar static int depth = 0;
6158071d4279SBram Moolenaar int r;
6159071d4279SBram Moolenaar
61600d6f5d97SBram Moolenaar // If ssp has a "containedin" list and "cur_si" is in it, return TRUE.
6161293ee4d4SBram Moolenaar if (cur_si != NULL && ssp->cont_in_list != NULL
6162293ee4d4SBram Moolenaar && !(cur_si->si_flags & HL_MATCH))
6163071d4279SBram Moolenaar {
61640d6f5d97SBram Moolenaar // Ignore transparent items without a contains argument. Double check
61650d6f5d97SBram Moolenaar // that we don't go back past the first one.
6166071d4279SBram Moolenaar while ((cur_si->si_flags & HL_TRANS_CONT)
6167071d4279SBram Moolenaar && cur_si > (stateitem_T *)(current_state.ga_data))
6168071d4279SBram Moolenaar --cur_si;
61690d6f5d97SBram Moolenaar // cur_si->si_idx is -1 for keywords, these never contain anything.
6170071d4279SBram Moolenaar if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
6171860cae1cSBram Moolenaar &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6172860cae1cSBram Moolenaar SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
6173071d4279SBram Moolenaar return TRUE;
6174071d4279SBram Moolenaar }
6175071d4279SBram Moolenaar
6176071d4279SBram Moolenaar if (list == NULL)
6177071d4279SBram Moolenaar return FALSE;
6178071d4279SBram Moolenaar
6179071d4279SBram Moolenaar /*
6180071d4279SBram Moolenaar * If list is ID_LIST_ALL, we are in a transparent item that isn't
6181071d4279SBram Moolenaar * inside anything. Only allow not-contained groups.
6182071d4279SBram Moolenaar */
6183071d4279SBram Moolenaar if (list == ID_LIST_ALL)
6184071d4279SBram Moolenaar return !contained;
6185071d4279SBram Moolenaar
6186071d4279SBram Moolenaar /*
6187071d4279SBram Moolenaar * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6188071d4279SBram Moolenaar * contains list. We also require that "id" is at the same ":syn include"
6189071d4279SBram Moolenaar * level as the list.
6190071d4279SBram Moolenaar */
6191071d4279SBram Moolenaar item = *list;
6192071d4279SBram Moolenaar if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6193071d4279SBram Moolenaar {
6194071d4279SBram Moolenaar if (item < SYNID_TOP)
6195071d4279SBram Moolenaar {
61960d6f5d97SBram Moolenaar // ALL or ALLBUT: accept all groups in the same file
6197071d4279SBram Moolenaar if (item - SYNID_ALLBUT != ssp->inc_tag)
6198071d4279SBram Moolenaar return FALSE;
6199071d4279SBram Moolenaar }
6200071d4279SBram Moolenaar else if (item < SYNID_CONTAINED)
6201071d4279SBram Moolenaar {
62020d6f5d97SBram Moolenaar // TOP: accept all not-contained groups in the same file
6203071d4279SBram Moolenaar if (item - SYNID_TOP != ssp->inc_tag || contained)
6204071d4279SBram Moolenaar return FALSE;
6205071d4279SBram Moolenaar }
6206071d4279SBram Moolenaar else
6207071d4279SBram Moolenaar {
62080d6f5d97SBram Moolenaar // CONTAINED: accept all contained groups in the same file
6209071d4279SBram Moolenaar if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6210071d4279SBram Moolenaar return FALSE;
6211071d4279SBram Moolenaar }
6212071d4279SBram Moolenaar item = *++list;
6213071d4279SBram Moolenaar retval = FALSE;
6214071d4279SBram Moolenaar }
6215071d4279SBram Moolenaar else
6216071d4279SBram Moolenaar retval = TRUE;
6217071d4279SBram Moolenaar
6218071d4279SBram Moolenaar /*
6219071d4279SBram Moolenaar * Return "retval" if id is in the contains list.
6220071d4279SBram Moolenaar */
6221071d4279SBram Moolenaar while (item != 0)
6222071d4279SBram Moolenaar {
6223071d4279SBram Moolenaar if (item == id)
6224071d4279SBram Moolenaar return retval;
6225071d4279SBram Moolenaar if (item >= SYNID_CLUSTER)
6226071d4279SBram Moolenaar {
6227860cae1cSBram Moolenaar scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
62280d6f5d97SBram Moolenaar // restrict recursiveness to 30 to avoid an endless loop for a
62290d6f5d97SBram Moolenaar // cluster that includes itself (indirectly)
6230071d4279SBram Moolenaar if (scl_list != NULL && depth < 30)
6231071d4279SBram Moolenaar {
6232071d4279SBram Moolenaar ++depth;
6233071d4279SBram Moolenaar r = in_id_list(NULL, scl_list, ssp, contained);
6234071d4279SBram Moolenaar --depth;
6235071d4279SBram Moolenaar if (r)
6236071d4279SBram Moolenaar return retval;
6237071d4279SBram Moolenaar }
6238071d4279SBram Moolenaar }
6239071d4279SBram Moolenaar item = *++list;
6240071d4279SBram Moolenaar }
6241071d4279SBram Moolenaar return !retval;
6242071d4279SBram Moolenaar }
6243071d4279SBram Moolenaar
6244071d4279SBram Moolenaar struct subcommand
6245071d4279SBram Moolenaar {
62460d6f5d97SBram Moolenaar char *name; // subcommand name
62470d6f5d97SBram Moolenaar void (*func)(exarg_T *, int); // function to call
6248071d4279SBram Moolenaar };
6249071d4279SBram Moolenaar
6250071d4279SBram Moolenaar static struct subcommand subcommands[] =
6251071d4279SBram Moolenaar {
6252071d4279SBram Moolenaar {"case", syn_cmd_case},
6253071d4279SBram Moolenaar {"clear", syn_cmd_clear},
6254071d4279SBram Moolenaar {"cluster", syn_cmd_cluster},
6255860cae1cSBram Moolenaar {"conceal", syn_cmd_conceal},
6256071d4279SBram Moolenaar {"enable", syn_cmd_enable},
6257e35a52aeSBram Moolenaar {"foldlevel", syn_cmd_foldlevel},
6258071d4279SBram Moolenaar {"include", syn_cmd_include},
6259b8060fe8SBram Moolenaar {"iskeyword", syn_cmd_iskeyword},
6260071d4279SBram Moolenaar {"keyword", syn_cmd_keyword},
6261071d4279SBram Moolenaar {"list", syn_cmd_list},
6262071d4279SBram Moolenaar {"manual", syn_cmd_manual},
6263071d4279SBram Moolenaar {"match", syn_cmd_match},
6264071d4279SBram Moolenaar {"on", syn_cmd_on},
6265071d4279SBram Moolenaar {"off", syn_cmd_off},
6266071d4279SBram Moolenaar {"region", syn_cmd_region},
6267071d4279SBram Moolenaar {"reset", syn_cmd_reset},
6268ce0842a6SBram Moolenaar {"spell", syn_cmd_spell},
6269071d4279SBram Moolenaar {"sync", syn_cmd_sync},
6270071d4279SBram Moolenaar {"", syn_cmd_list},
6271071d4279SBram Moolenaar {NULL, NULL}
6272071d4279SBram Moolenaar };
6273071d4279SBram Moolenaar
6274071d4279SBram Moolenaar /*
6275071d4279SBram Moolenaar * ":syntax".
6276071d4279SBram Moolenaar * This searches the subcommands[] table for the subcommand name, and calls a
6277071d4279SBram Moolenaar * syntax_subcommand() function to do the rest.
6278071d4279SBram Moolenaar */
6279071d4279SBram Moolenaar void
ex_syntax(exarg_T * eap)6280764b23c8SBram Moolenaar ex_syntax(exarg_T *eap)
6281071d4279SBram Moolenaar {
6282071d4279SBram Moolenaar char_u *arg = eap->arg;
6283071d4279SBram Moolenaar char_u *subcmd_end;
6284071d4279SBram Moolenaar char_u *subcmd_name;
6285071d4279SBram Moolenaar int i;
6286071d4279SBram Moolenaar
6287071d4279SBram Moolenaar syn_cmdlinep = eap->cmdlinep;
6288071d4279SBram Moolenaar
62890d6f5d97SBram Moolenaar // isolate subcommand name
6290071d4279SBram Moolenaar for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6291071d4279SBram Moolenaar ;
629271ccd03eSBram Moolenaar subcmd_name = vim_strnsave(arg, subcmd_end - arg);
6293071d4279SBram Moolenaar if (subcmd_name != NULL)
6294071d4279SBram Moolenaar {
62950d6f5d97SBram Moolenaar if (eap->skip) // skip error messages for all subcommands
6296071d4279SBram Moolenaar ++emsg_skip;
6297071d4279SBram Moolenaar for (i = 0; ; ++i)
6298071d4279SBram Moolenaar {
6299071d4279SBram Moolenaar if (subcommands[i].name == NULL)
6300071d4279SBram Moolenaar {
6301f9e3e09fSBram Moolenaar semsg(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6302071d4279SBram Moolenaar break;
6303071d4279SBram Moolenaar }
6304071d4279SBram Moolenaar if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6305071d4279SBram Moolenaar {
6306071d4279SBram Moolenaar eap->arg = skipwhite(subcmd_end);
6307071d4279SBram Moolenaar (subcommands[i].func)(eap, FALSE);
6308071d4279SBram Moolenaar break;
6309071d4279SBram Moolenaar }
6310071d4279SBram Moolenaar }
6311071d4279SBram Moolenaar vim_free(subcmd_name);
6312071d4279SBram Moolenaar if (eap->skip)
6313071d4279SBram Moolenaar --emsg_skip;
6314071d4279SBram Moolenaar }
6315071d4279SBram Moolenaar }
6316071d4279SBram Moolenaar
6317860cae1cSBram Moolenaar void
ex_ownsyntax(exarg_T * eap)6318764b23c8SBram Moolenaar ex_ownsyntax(exarg_T *eap)
6319071d4279SBram Moolenaar {
63201950c352SBram Moolenaar char_u *old_value;
63211950c352SBram Moolenaar char_u *new_value;
63221950c352SBram Moolenaar
6323860cae1cSBram Moolenaar if (curwin->w_s == &curwin->w_buffer->b_s)
6324860cae1cSBram Moolenaar {
6325c799fe20SBram Moolenaar curwin->w_s = ALLOC_ONE(synblock_T);
6326860cae1cSBram Moolenaar memset(curwin->w_s, 0, sizeof(synblock_T));
6327670acbc7SBram Moolenaar hash_init(&curwin->w_s->b_keywtab);
6328670acbc7SBram Moolenaar hash_init(&curwin->w_s->b_keywtab_ic);
6329860cae1cSBram Moolenaar #ifdef FEAT_SPELL
63300d6f5d97SBram Moolenaar // TODO: keep the spell checking as it was.
63310d6f5d97SBram Moolenaar curwin->w_p_spell = FALSE; // No spell checking
6332d1f76afaSBram Moolenaar // make sure option values are "empty_option" instead of NULL
6333860cae1cSBram Moolenaar clear_string_option(&curwin->w_s->b_p_spc);
6334860cae1cSBram Moolenaar clear_string_option(&curwin->w_s->b_p_spf);
6335860cae1cSBram Moolenaar clear_string_option(&curwin->w_s->b_p_spl);
6336d1f76afaSBram Moolenaar clear_string_option(&curwin->w_s->b_p_spo);
6337860cae1cSBram Moolenaar #endif
6338b8060fe8SBram Moolenaar clear_string_option(&curwin->w_s->b_syn_isk);
6339860cae1cSBram Moolenaar }
63401950c352SBram Moolenaar
63410d6f5d97SBram Moolenaar // save value of b:current_syntax
63421950c352SBram Moolenaar old_value = get_var_value((char_u *)"b:current_syntax");
63431950c352SBram Moolenaar if (old_value != NULL)
63441950c352SBram Moolenaar old_value = vim_strsave(old_value);
63451950c352SBram Moolenaar
63460d6f5d97SBram Moolenaar // Apply the "syntax" autocommand event, this finds and loads the syntax
63470d6f5d97SBram Moolenaar // file.
6348860cae1cSBram Moolenaar apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
63491950c352SBram Moolenaar
63500d6f5d97SBram Moolenaar // move value of b:current_syntax to w:current_syntax
63511950c352SBram Moolenaar new_value = get_var_value((char_u *)"b:current_syntax");
6352e0c6a656SBram Moolenaar if (new_value != NULL)
63531950c352SBram Moolenaar set_internal_string_var((char_u *)"w:current_syntax", new_value);
63541950c352SBram Moolenaar
63550d6f5d97SBram Moolenaar // restore value of b:current_syntax
6356e0c6a656SBram Moolenaar if (old_value == NULL)
6357e0c6a656SBram Moolenaar do_unlet((char_u *)"b:current_syntax", TRUE);
6358e0c6a656SBram Moolenaar else
63591950c352SBram Moolenaar {
63601950c352SBram Moolenaar set_internal_string_var((char_u *)"b:current_syntax", old_value);
63611950c352SBram Moolenaar vim_free(old_value);
63621950c352SBram Moolenaar }
6363860cae1cSBram Moolenaar }
6364860cae1cSBram Moolenaar
6365860cae1cSBram Moolenaar int
syntax_present(win_T * win)6366764b23c8SBram Moolenaar syntax_present(win_T *win)
6367860cae1cSBram Moolenaar {
6368860cae1cSBram Moolenaar return (win->w_s->b_syn_patterns.ga_len != 0
6369860cae1cSBram Moolenaar || win->w_s->b_syn_clusters.ga_len != 0
6370860cae1cSBram Moolenaar || win->w_s->b_keywtab.ht_used > 0
6371860cae1cSBram Moolenaar || win->w_s->b_keywtab_ic.ht_used > 0);
6372071d4279SBram Moolenaar }
6373071d4279SBram Moolenaar
6374071d4279SBram Moolenaar
6375071d4279SBram Moolenaar static enum
6376071d4279SBram Moolenaar {
63770d6f5d97SBram Moolenaar EXP_SUBCMD, // expand ":syn" sub-commands
63780d6f5d97SBram Moolenaar EXP_CASE, // expand ":syn case" arguments
63790d6f5d97SBram Moolenaar EXP_SPELL, // expand ":syn spell" arguments
63800d6f5d97SBram Moolenaar EXP_SYNC // expand ":syn sync" arguments
6381071d4279SBram Moolenaar } expand_what;
6382071d4279SBram Moolenaar
63834f688587SBram Moolenaar /*
63844f688587SBram Moolenaar * Reset include_link, include_default, include_none to 0.
63854f688587SBram Moolenaar * Called when we are done expanding.
63864f688587SBram Moolenaar */
63874f688587SBram Moolenaar void
reset_expand_highlight(void)6388764b23c8SBram Moolenaar reset_expand_highlight(void)
63894f688587SBram Moolenaar {
63904f688587SBram Moolenaar include_link = include_default = include_none = 0;
63914f688587SBram Moolenaar }
63924f688587SBram Moolenaar
63934f688587SBram Moolenaar /*
63944f688587SBram Moolenaar * Handle command line completion for :match and :echohl command: Add "None"
63954f688587SBram Moolenaar * as highlight group.
63964f688587SBram Moolenaar */
63974f688587SBram Moolenaar void
set_context_in_echohl_cmd(expand_T * xp,char_u * arg)6398764b23c8SBram Moolenaar set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
63994f688587SBram Moolenaar {
64004f688587SBram Moolenaar xp->xp_context = EXPAND_HIGHLIGHT;
64014f688587SBram Moolenaar xp->xp_pattern = arg;
64024f688587SBram Moolenaar include_none = 1;
64034f688587SBram Moolenaar }
6404071d4279SBram Moolenaar
6405071d4279SBram Moolenaar /*
6406071d4279SBram Moolenaar * Handle command line completion for :syntax command.
6407071d4279SBram Moolenaar */
6408071d4279SBram Moolenaar void
set_context_in_syntax_cmd(expand_T * xp,char_u * arg)6409764b23c8SBram Moolenaar set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
6410071d4279SBram Moolenaar {
6411071d4279SBram Moolenaar char_u *p;
6412071d4279SBram Moolenaar
64130d6f5d97SBram Moolenaar // Default: expand subcommands
6414071d4279SBram Moolenaar xp->xp_context = EXPAND_SYNTAX;
6415071d4279SBram Moolenaar expand_what = EXP_SUBCMD;
6416071d4279SBram Moolenaar xp->xp_pattern = arg;
64174f688587SBram Moolenaar include_link = 0;
64184f688587SBram Moolenaar include_default = 0;
6419071d4279SBram Moolenaar
64200d6f5d97SBram Moolenaar // (part of) subcommand already typed
6421071d4279SBram Moolenaar if (*arg != NUL)
6422071d4279SBram Moolenaar {
6423071d4279SBram Moolenaar p = skiptowhite(arg);
64240d6f5d97SBram Moolenaar if (*p != NUL) // past first word
6425071d4279SBram Moolenaar {
6426071d4279SBram Moolenaar xp->xp_pattern = skipwhite(p);
6427071d4279SBram Moolenaar if (*skiptowhite(xp->xp_pattern) != NUL)
6428071d4279SBram Moolenaar xp->xp_context = EXPAND_NOTHING;
6429071d4279SBram Moolenaar else if (STRNICMP(arg, "case", p - arg) == 0)
6430071d4279SBram Moolenaar expand_what = EXP_CASE;
64312d028390SBram Moolenaar else if (STRNICMP(arg, "spell", p - arg) == 0)
64322d028390SBram Moolenaar expand_what = EXP_SPELL;
64332d028390SBram Moolenaar else if (STRNICMP(arg, "sync", p - arg) == 0)
64342d028390SBram Moolenaar expand_what = EXP_SYNC;
6435071d4279SBram Moolenaar else if ( STRNICMP(arg, "keyword", p - arg) == 0
6436071d4279SBram Moolenaar || STRNICMP(arg, "region", p - arg) == 0
6437071d4279SBram Moolenaar || STRNICMP(arg, "match", p - arg) == 0
6438071d4279SBram Moolenaar || STRNICMP(arg, "list", p - arg) == 0)
6439071d4279SBram Moolenaar xp->xp_context = EXPAND_HIGHLIGHT;
6440071d4279SBram Moolenaar else
6441071d4279SBram Moolenaar xp->xp_context = EXPAND_NOTHING;
6442071d4279SBram Moolenaar }
6443071d4279SBram Moolenaar }
6444071d4279SBram Moolenaar }
6445071d4279SBram Moolenaar
6446071d4279SBram Moolenaar /*
6447071d4279SBram Moolenaar * Function given to ExpandGeneric() to obtain the list syntax names for
6448071d4279SBram Moolenaar * expansion.
6449071d4279SBram Moolenaar */
6450071d4279SBram Moolenaar char_u *
get_syntax_name(expand_T * xp UNUSED,int idx)6451764b23c8SBram Moolenaar get_syntax_name(expand_T *xp UNUSED, int idx)
6452071d4279SBram Moolenaar {
64532d028390SBram Moolenaar switch (expand_what)
64542d028390SBram Moolenaar {
64552d028390SBram Moolenaar case EXP_SUBCMD:
6456071d4279SBram Moolenaar return (char_u *)subcommands[idx].name;
64572d028390SBram Moolenaar case EXP_CASE:
64582d028390SBram Moolenaar {
64592d028390SBram Moolenaar static char *case_args[] = {"match", "ignore", NULL};
6460071d4279SBram Moolenaar return (char_u *)case_args[idx];
6461071d4279SBram Moolenaar }
64622d028390SBram Moolenaar case EXP_SPELL:
64632d028390SBram Moolenaar {
64642d028390SBram Moolenaar static char *spell_args[] =
64652d028390SBram Moolenaar {"toplevel", "notoplevel", "default", NULL};
64662d028390SBram Moolenaar return (char_u *)spell_args[idx];
64672d028390SBram Moolenaar }
64682d028390SBram Moolenaar case EXP_SYNC:
64692d028390SBram Moolenaar {
64702d028390SBram Moolenaar static char *sync_args[] =
64712d028390SBram Moolenaar {"ccomment", "clear", "fromstart",
64722d028390SBram Moolenaar "linebreaks=", "linecont", "lines=", "match",
64732d028390SBram Moolenaar "maxlines=", "minlines=", "region", NULL};
64742d028390SBram Moolenaar return (char_u *)sync_args[idx];
64752d028390SBram Moolenaar }
64762d028390SBram Moolenaar }
64772d028390SBram Moolenaar return NULL;
64782d028390SBram Moolenaar }
6479071d4279SBram Moolenaar
6480071d4279SBram Moolenaar
6481071d4279SBram Moolenaar /*
6482071d4279SBram Moolenaar * Function called for expression evaluation: get syntax ID at file position.
6483071d4279SBram Moolenaar */
6484071d4279SBram Moolenaar int
syn_get_id(win_T * wp,long lnum,colnr_T col,int trans,int * spellp,int keep_state)6485764b23c8SBram Moolenaar syn_get_id(
6486764b23c8SBram Moolenaar win_T *wp,
6487764b23c8SBram Moolenaar long lnum,
6488764b23c8SBram Moolenaar colnr_T col,
64890d6f5d97SBram Moolenaar int trans, // remove transparency
64900d6f5d97SBram Moolenaar int *spellp, // return: can do spell checking
64910d6f5d97SBram Moolenaar int keep_state) // keep state of char at "col"
6492071d4279SBram Moolenaar {
64930d6f5d97SBram Moolenaar // When the position is not after the current position and in the same
64940d6f5d97SBram Moolenaar // line of the same buffer, need to restart parsing.
649581f1ecbcSBram Moolenaar if (wp->w_buffer != syn_buf
6496071d4279SBram Moolenaar || lnum != current_lnum
64979d0ec2e4SBram Moolenaar || col < current_col)
6498f3d769a5SBram Moolenaar syntax_start(wp, lnum);
64996773a348SBram Moolenaar else if (wp->w_buffer == syn_buf
65006773a348SBram Moolenaar && lnum == current_lnum
65016773a348SBram Moolenaar && col > current_col)
65020d6f5d97SBram Moolenaar // next_match may not be correct when moving around, e.g. with the
65030d6f5d97SBram Moolenaar // "skip" expression in searchpair()
65046773a348SBram Moolenaar next_match_idx = -1;
6505071d4279SBram Moolenaar
650627c735b2SBram Moolenaar (void)get_syntax_attr(col, spellp, keep_state);
6507071d4279SBram Moolenaar
6508071d4279SBram Moolenaar return (trans ? current_trans_id : current_id);
6509071d4279SBram Moolenaar }
6510071d4279SBram Moolenaar
6511860cae1cSBram Moolenaar #if defined(FEAT_CONCEAL) || defined(PROTO)
6512860cae1cSBram Moolenaar /*
651327c735b2SBram Moolenaar * Get extra information about the syntax item. Must be called right after
651427c735b2SBram Moolenaar * get_syntax_attr().
6515ffbbcb59SBram Moolenaar * Stores the current item sequence nr in "*seqnrp".
651627c735b2SBram Moolenaar * Returns the current flags.
651727c735b2SBram Moolenaar */
651827c735b2SBram Moolenaar int
get_syntax_info(int * seqnrp)6519764b23c8SBram Moolenaar get_syntax_info(int *seqnrp)
652027c735b2SBram Moolenaar {
6521ffbbcb59SBram Moolenaar *seqnrp = current_seqnr;
652227c735b2SBram Moolenaar return current_flags;
652327c735b2SBram Moolenaar }
652427c735b2SBram Moolenaar
652527c735b2SBram Moolenaar /*
6526860cae1cSBram Moolenaar * Return conceal substitution character
6527860cae1cSBram Moolenaar */
6528860cae1cSBram Moolenaar int
syn_get_sub_char(void)6529764b23c8SBram Moolenaar syn_get_sub_char(void)
6530860cae1cSBram Moolenaar {
6531860cae1cSBram Moolenaar return current_sub_char;
6532860cae1cSBram Moolenaar }
6533860cae1cSBram Moolenaar #endif
6534860cae1cSBram Moolenaar
65359d188ab0SBram Moolenaar #if defined(FEAT_EVAL) || defined(PROTO)
65369d188ab0SBram Moolenaar /*
65379d188ab0SBram Moolenaar * Return the syntax ID at position "i" in the current stack.
65389d188ab0SBram Moolenaar * The caller must have called syn_get_id() before to fill the stack.
65399d188ab0SBram Moolenaar * Returns -1 when "i" is out of range.
65409d188ab0SBram Moolenaar */
65419d188ab0SBram Moolenaar int
syn_get_stack_item(int i)6542764b23c8SBram Moolenaar syn_get_stack_item(int i)
65439d188ab0SBram Moolenaar {
65449d188ab0SBram Moolenaar if (i >= current_state.ga_len)
654556cefaf1SBram Moolenaar {
65460d6f5d97SBram Moolenaar // Need to invalidate the state, because we didn't properly finish it
65470d6f5d97SBram Moolenaar // for the last character, "keep_state" was TRUE.
654856cefaf1SBram Moolenaar invalidate_current_state();
654956cefaf1SBram Moolenaar current_col = MAXCOL;
65509d188ab0SBram Moolenaar return -1;
655156cefaf1SBram Moolenaar }
65529d188ab0SBram Moolenaar return CUR_STATE(i).si_id;
65539d188ab0SBram Moolenaar }
65549d188ab0SBram Moolenaar #endif
65559d188ab0SBram Moolenaar
6556071d4279SBram Moolenaar #if defined(FEAT_FOLDING) || defined(PROTO)
6557e35a52aeSBram Moolenaar static int
syn_cur_foldlevel(void)6558e35a52aeSBram Moolenaar syn_cur_foldlevel(void)
6559e35a52aeSBram Moolenaar {
6560e35a52aeSBram Moolenaar int level = 0;
6561e35a52aeSBram Moolenaar int i;
6562e35a52aeSBram Moolenaar
6563e35a52aeSBram Moolenaar for (i = 0; i < current_state.ga_len; ++i)
6564e35a52aeSBram Moolenaar if (CUR_STATE(i).si_flags & HL_FOLD)
6565e35a52aeSBram Moolenaar ++level;
6566e35a52aeSBram Moolenaar return level;
6567e35a52aeSBram Moolenaar }
6568e35a52aeSBram Moolenaar
6569071d4279SBram Moolenaar /*
6570071d4279SBram Moolenaar * Function called to get folding level for line "lnum" in window "wp".
6571071d4279SBram Moolenaar */
6572071d4279SBram Moolenaar int
syn_get_foldlevel(win_T * wp,long lnum)6573764b23c8SBram Moolenaar syn_get_foldlevel(win_T *wp, long lnum)
6574071d4279SBram Moolenaar {
6575071d4279SBram Moolenaar int level = 0;
6576e35a52aeSBram Moolenaar int low_level;
6577e35a52aeSBram Moolenaar int cur_level;
6578071d4279SBram Moolenaar
65790d6f5d97SBram Moolenaar // Return quickly when there are no fold items at all.
658006f1ed2fSBram Moolenaar if (wp->w_s->b_syn_folditems != 0
658106f1ed2fSBram Moolenaar && !wp->w_s->b_syn_error
658206f1ed2fSBram Moolenaar # ifdef SYN_TIME_LIMIT
658306f1ed2fSBram Moolenaar && !wp->w_s->b_syn_slow
658406f1ed2fSBram Moolenaar # endif
658506f1ed2fSBram Moolenaar )
6586071d4279SBram Moolenaar {
6587f3d769a5SBram Moolenaar syntax_start(wp, lnum);
6588071d4279SBram Moolenaar
6589e35a52aeSBram Moolenaar // Start with the fold level at the start of the line.
6590e35a52aeSBram Moolenaar level = syn_cur_foldlevel();
6591e35a52aeSBram Moolenaar
6592e35a52aeSBram Moolenaar if (wp->w_s->b_syn_foldlevel == SYNFLD_MINIMUM)
6593e35a52aeSBram Moolenaar {
6594e35a52aeSBram Moolenaar // Find the lowest fold level that is followed by a higher one.
6595e35a52aeSBram Moolenaar cur_level = level;
6596e35a52aeSBram Moolenaar low_level = cur_level;
6597e35a52aeSBram Moolenaar while (!current_finished)
6598e35a52aeSBram Moolenaar {
6599e35a52aeSBram Moolenaar (void)syn_current_attr(FALSE, FALSE, NULL, FALSE);
6600e35a52aeSBram Moolenaar cur_level = syn_cur_foldlevel();
6601e35a52aeSBram Moolenaar if (cur_level < low_level)
6602e35a52aeSBram Moolenaar low_level = cur_level;
6603e35a52aeSBram Moolenaar else if (cur_level > low_level)
6604e35a52aeSBram Moolenaar level = low_level;
6605e35a52aeSBram Moolenaar ++current_col;
6606e35a52aeSBram Moolenaar }
6607e35a52aeSBram Moolenaar }
6608071d4279SBram Moolenaar }
6609071d4279SBram Moolenaar if (level > wp->w_p_fdn)
661074c596b5SBram Moolenaar {
6611071d4279SBram Moolenaar level = wp->w_p_fdn;
661274c596b5SBram Moolenaar if (level < 0)
661374c596b5SBram Moolenaar level = 0;
661474c596b5SBram Moolenaar }
6615071d4279SBram Moolenaar return level;
6616071d4279SBram Moolenaar }
6617071d4279SBram Moolenaar #endif
6618071d4279SBram Moolenaar
661901615491SBram Moolenaar #if defined(FEAT_PROFILE) || defined(PROTO)
66208a7f5a2dSBram Moolenaar /*
66218a7f5a2dSBram Moolenaar * ":syntime".
66228a7f5a2dSBram Moolenaar */
66238a7f5a2dSBram Moolenaar void
ex_syntime(exarg_T * eap)6624764b23c8SBram Moolenaar ex_syntime(exarg_T *eap)
66258a7f5a2dSBram Moolenaar {
66268a7f5a2dSBram Moolenaar if (STRCMP(eap->arg, "on") == 0)
66278a7f5a2dSBram Moolenaar syn_time_on = TRUE;
66288a7f5a2dSBram Moolenaar else if (STRCMP(eap->arg, "off") == 0)
66298a7f5a2dSBram Moolenaar syn_time_on = FALSE;
66308a7f5a2dSBram Moolenaar else if (STRCMP(eap->arg, "clear") == 0)
66318a7f5a2dSBram Moolenaar syntime_clear();
66328a7f5a2dSBram Moolenaar else if (STRCMP(eap->arg, "report") == 0)
66338a7f5a2dSBram Moolenaar syntime_report();
66348a7f5a2dSBram Moolenaar else
6635f9e3e09fSBram Moolenaar semsg(_(e_invarg2), eap->arg);
66368a7f5a2dSBram Moolenaar }
66378a7f5a2dSBram Moolenaar
66388a7f5a2dSBram Moolenaar static void
syn_clear_time(syn_time_T * st)6639764b23c8SBram Moolenaar syn_clear_time(syn_time_T *st)
66408a7f5a2dSBram Moolenaar {
66418a7f5a2dSBram Moolenaar profile_zero(&st->total);
66428a7f5a2dSBram Moolenaar profile_zero(&st->slowest);
66438a7f5a2dSBram Moolenaar st->count = 0;
66448a7f5a2dSBram Moolenaar st->match = 0;
66458a7f5a2dSBram Moolenaar }
66468a7f5a2dSBram Moolenaar
66478a7f5a2dSBram Moolenaar /*
66488a7f5a2dSBram Moolenaar * Clear the syntax timing for the current buffer.
66498a7f5a2dSBram Moolenaar */
66508a7f5a2dSBram Moolenaar static void
syntime_clear(void)6651764b23c8SBram Moolenaar syntime_clear(void)
66528a7f5a2dSBram Moolenaar {
66538a7f5a2dSBram Moolenaar int idx;
66548a7f5a2dSBram Moolenaar synpat_T *spp;
66558a7f5a2dSBram Moolenaar
66568a7f5a2dSBram Moolenaar if (!syntax_present(curwin))
66578a7f5a2dSBram Moolenaar {
665832526b3cSBram Moolenaar msg(_(msg_no_items));
66598a7f5a2dSBram Moolenaar return;
66608a7f5a2dSBram Moolenaar }
66618a7f5a2dSBram Moolenaar for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
66628a7f5a2dSBram Moolenaar {
66638a7f5a2dSBram Moolenaar spp = &(SYN_ITEMS(curwin->w_s)[idx]);
66648a7f5a2dSBram Moolenaar syn_clear_time(&spp->sp_time);
66658a7f5a2dSBram Moolenaar }
66668a7f5a2dSBram Moolenaar }
66678a7f5a2dSBram Moolenaar
6668cd9c4626SBram Moolenaar /*
6669cd9c4626SBram Moolenaar * Function given to ExpandGeneric() to obtain the possible arguments of the
6670cd9c4626SBram Moolenaar * ":syntime {on,off,clear,report}" command.
6671cd9c4626SBram Moolenaar */
6672cd9c4626SBram Moolenaar char_u *
get_syntime_arg(expand_T * xp UNUSED,int idx)6673764b23c8SBram Moolenaar get_syntime_arg(expand_T *xp UNUSED, int idx)
6674cd9c4626SBram Moolenaar {
6675cd9c4626SBram Moolenaar switch (idx)
6676cd9c4626SBram Moolenaar {
6677cd9c4626SBram Moolenaar case 0: return (char_u *)"on";
6678cd9c4626SBram Moolenaar case 1: return (char_u *)"off";
6679cd9c4626SBram Moolenaar case 2: return (char_u *)"clear";
6680cd9c4626SBram Moolenaar case 3: return (char_u *)"report";
6681cd9c4626SBram Moolenaar }
6682cd9c4626SBram Moolenaar return NULL;
6683cd9c4626SBram Moolenaar }
6684cd9c4626SBram Moolenaar
66858a7f5a2dSBram Moolenaar typedef struct
66868a7f5a2dSBram Moolenaar {
66878a7f5a2dSBram Moolenaar proftime_T total;
66888a7f5a2dSBram Moolenaar int count;
66898a7f5a2dSBram Moolenaar int match;
66908a7f5a2dSBram Moolenaar proftime_T slowest;
66918a7f5a2dSBram Moolenaar proftime_T average;
66928a7f5a2dSBram Moolenaar int id;
66938a7f5a2dSBram Moolenaar char_u *pattern;
66948a7f5a2dSBram Moolenaar } time_entry_T;
66958a7f5a2dSBram Moolenaar
66968a7f5a2dSBram Moolenaar static int
syn_compare_syntime(const void * v1,const void * v2)6697764b23c8SBram Moolenaar syn_compare_syntime(const void *v1, const void *v2)
66988a7f5a2dSBram Moolenaar {
66998a7f5a2dSBram Moolenaar const time_entry_T *s1 = v1;
67008a7f5a2dSBram Moolenaar const time_entry_T *s2 = v2;
67018a7f5a2dSBram Moolenaar
67028a7f5a2dSBram Moolenaar return profile_cmp(&s1->total, &s2->total);
67038a7f5a2dSBram Moolenaar }
67048a7f5a2dSBram Moolenaar
67058a7f5a2dSBram Moolenaar /*
67068a7f5a2dSBram Moolenaar * Clear the syntax timing for the current buffer.
67078a7f5a2dSBram Moolenaar */
67088a7f5a2dSBram Moolenaar static void
syntime_report(void)6709764b23c8SBram Moolenaar syntime_report(void)
67108a7f5a2dSBram Moolenaar {
67118a7f5a2dSBram Moolenaar int idx;
67128a7f5a2dSBram Moolenaar synpat_T *spp;
671381319678SBram Moolenaar # if defined(FEAT_RELTIME) && defined(FEAT_FLOAT)
67148a7f5a2dSBram Moolenaar proftime_T tm;
6715cd9c4626SBram Moolenaar # endif
67168a7f5a2dSBram Moolenaar int len;
67178a7f5a2dSBram Moolenaar proftime_T total_total;
67188a7f5a2dSBram Moolenaar int total_count = 0;
67198a7f5a2dSBram Moolenaar garray_T ga;
67208a7f5a2dSBram Moolenaar time_entry_T *p;
67218a7f5a2dSBram Moolenaar
67228a7f5a2dSBram Moolenaar if (!syntax_present(curwin))
67238a7f5a2dSBram Moolenaar {
672432526b3cSBram Moolenaar msg(_(msg_no_items));
67258a7f5a2dSBram Moolenaar return;
67268a7f5a2dSBram Moolenaar }
67278a7f5a2dSBram Moolenaar
67288a7f5a2dSBram Moolenaar ga_init2(&ga, sizeof(time_entry_T), 50);
67298a7f5a2dSBram Moolenaar profile_zero(&total_total);
67308a7f5a2dSBram Moolenaar for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
67318a7f5a2dSBram Moolenaar {
67328a7f5a2dSBram Moolenaar spp = &(SYN_ITEMS(curwin->w_s)[idx]);
67338a7f5a2dSBram Moolenaar if (spp->sp_time.count > 0)
67348a7f5a2dSBram Moolenaar {
6735cde88547SBram Moolenaar (void)ga_grow(&ga, 1);
67368a7f5a2dSBram Moolenaar p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
67378a7f5a2dSBram Moolenaar p->total = spp->sp_time.total;
67388a7f5a2dSBram Moolenaar profile_add(&total_total, &spp->sp_time.total);
67398a7f5a2dSBram Moolenaar p->count = spp->sp_time.count;
67408a7f5a2dSBram Moolenaar p->match = spp->sp_time.match;
67418a7f5a2dSBram Moolenaar total_count += spp->sp_time.count;
67428a7f5a2dSBram Moolenaar p->slowest = spp->sp_time.slowest;
674381319678SBram Moolenaar # if defined(FEAT_RELTIME) && defined(FEAT_FLOAT)
67448a7f5a2dSBram Moolenaar profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
67458a7f5a2dSBram Moolenaar p->average = tm;
67468a7f5a2dSBram Moolenaar # endif
67478a7f5a2dSBram Moolenaar p->id = spp->sp_syn.id;
67488a7f5a2dSBram Moolenaar p->pattern = spp->sp_pattern;
67498a7f5a2dSBram Moolenaar ++ga.ga_len;
67508a7f5a2dSBram Moolenaar }
67518a7f5a2dSBram Moolenaar }
67528a7f5a2dSBram Moolenaar
67530d6f5d97SBram Moolenaar // Sort on total time. Skip if there are no items to avoid passing NULL
67540d6f5d97SBram Moolenaar // pointer to qsort().
6755a216255aSBram Moolenaar if (ga.ga_len > 1)
67564e31296fSBram Moolenaar qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
67574e31296fSBram Moolenaar syn_compare_syntime);
67588a7f5a2dSBram Moolenaar
675932526b3cSBram Moolenaar msg_puts_title(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
676032526b3cSBram Moolenaar msg_puts("\n");
67618a7f5a2dSBram Moolenaar for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
67628a7f5a2dSBram Moolenaar {
67638a7f5a2dSBram Moolenaar p = ((time_entry_T *)ga.ga_data) + idx;
67648a7f5a2dSBram Moolenaar
676532526b3cSBram Moolenaar msg_puts(profile_msg(&p->total));
67660d6f5d97SBram Moolenaar msg_puts(" "); // make sure there is always a separating space
67678a7f5a2dSBram Moolenaar msg_advance(13);
67688a7f5a2dSBram Moolenaar msg_outnum(p->count);
676932526b3cSBram Moolenaar msg_puts(" ");
67708a7f5a2dSBram Moolenaar msg_advance(20);
67718a7f5a2dSBram Moolenaar msg_outnum(p->match);
677232526b3cSBram Moolenaar msg_puts(" ");
67738a7f5a2dSBram Moolenaar msg_advance(26);
677432526b3cSBram Moolenaar msg_puts(profile_msg(&p->slowest));
677532526b3cSBram Moolenaar msg_puts(" ");
67768a7f5a2dSBram Moolenaar msg_advance(38);
67778a7f5a2dSBram Moolenaar # ifdef FEAT_FLOAT
677832526b3cSBram Moolenaar msg_puts(profile_msg(&p->average));
677932526b3cSBram Moolenaar msg_puts(" ");
67808a7f5a2dSBram Moolenaar # endif
67818a7f5a2dSBram Moolenaar msg_advance(50);
67822ac6e82aSBram Moolenaar msg_outtrans(highlight_group_name(p->id - 1));
678332526b3cSBram Moolenaar msg_puts(" ");
67848a7f5a2dSBram Moolenaar
67858a7f5a2dSBram Moolenaar msg_advance(69);
67868a7f5a2dSBram Moolenaar if (Columns < 80)
67870d6f5d97SBram Moolenaar len = 20; // will wrap anyway
67888a7f5a2dSBram Moolenaar else
67898a7f5a2dSBram Moolenaar len = Columns - 70;
67908a7f5a2dSBram Moolenaar if (len > (int)STRLEN(p->pattern))
67918a7f5a2dSBram Moolenaar len = (int)STRLEN(p->pattern);
67928a7f5a2dSBram Moolenaar msg_outtrans_len(p->pattern, len);
679332526b3cSBram Moolenaar msg_puts("\n");
67948a7f5a2dSBram Moolenaar }
679545fc539fSBram Moolenaar ga_clear(&ga);
67968a7f5a2dSBram Moolenaar if (!got_int)
67978a7f5a2dSBram Moolenaar {
679832526b3cSBram Moolenaar msg_puts("\n");
679932526b3cSBram Moolenaar msg_puts(profile_msg(&total_total));
68008a7f5a2dSBram Moolenaar msg_advance(13);
68018a7f5a2dSBram Moolenaar msg_outnum(total_count);
680232526b3cSBram Moolenaar msg_puts("\n");
68038a7f5a2dSBram Moolenaar }
68048a7f5a2dSBram Moolenaar }
68058a7f5a2dSBram Moolenaar #endif
68068a7f5a2dSBram Moolenaar
68070d6f5d97SBram Moolenaar #endif // FEAT_SYN_HL
6808