blob: 09491450ba9514366be1c57ee133bf5ebc9e27f1 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * syntax.c: code for syntax highlighting
12 */
13
14#include "vim.h"
15
16/*
17 * Structure that stores information about a highlight group.
18 * The ID of a highlight group is also called group ID. It is the index in
19 * the highlight_ga array PLUS ONE.
20 */
21struct hl_group
22{
23 char_u *sg_name; /* highlight group name */
24 char_u *sg_name_u; /* uppercase of sg_name */
Bram Moolenaard61e8aa2017-01-17 17:44:46 +010025 int sg_cleared; /* "hi clear" was used */
Bram Moolenaar071d4272004-06-13 20:20:40 +000026/* for normal terminals */
27 int sg_term; /* "term=" highlighting attributes */
28 char_u *sg_start; /* terminal string for start highl */
29 char_u *sg_stop; /* terminal string for stop highl */
30 int sg_term_attr; /* Screen attr for term mode */
31/* for color terminals */
32 int sg_cterm; /* "cterm=" highlighting attr */
33 int sg_cterm_bold; /* bold attr was set for light color */
34 int sg_cterm_fg; /* terminal fg color number + 1 */
35 int sg_cterm_bg; /* terminal bg color number + 1 */
36 int sg_cterm_attr; /* Screen attr for color term mode */
Bram Moolenaar071d4272004-06-13 20:20:40 +000037/* for when using the GUI */
Bram Moolenaar61be73b2016-04-29 22:59:22 +020038#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +000039 guicolor_T sg_gui_fg; /* GUI foreground color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000040 guicolor_T sg_gui_bg; /* GUI background color handle */
Bram Moolenaar8a633e32016-04-21 21:10:14 +020041#endif
42#ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +000043 guicolor_T sg_gui_sp; /* GUI special color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000044 GuiFont sg_font; /* GUI font handle */
45#ifdef FEAT_XFONTSET
46 GuiFontset sg_fontset; /* GUI fontset handle */
47#endif
48 char_u *sg_font_name; /* GUI font or fontset name */
49 int sg_gui_attr; /* Screen attr for GUI mode */
50#endif
Bram Moolenaar61623362010-07-14 22:04:22 +020051#if defined(FEAT_GUI) || defined(FEAT_EVAL)
52/* Store the sp color name for the GUI or synIDattr() */
53 int sg_gui; /* "gui=" highlighting attributes */
54 char_u *sg_gui_fg_name;/* GUI foreground color name */
55 char_u *sg_gui_bg_name;/* GUI background color name */
56 char_u *sg_gui_sp_name;/* GUI special color name */
57#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000058 int sg_link; /* link to this highlight group ID */
59 int sg_set; /* combination of SG_* flags */
Bram Moolenaar661b1822005-07-28 22:36:45 +000060#ifdef FEAT_EVAL
Bram Moolenaarf29c1c62018-09-10 21:05:02 +020061 sctx_T sg_script_ctx; /* script in which the group was last set */
Bram Moolenaar661b1822005-07-28 22:36:45 +000062#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000063};
64
65#define SG_TERM 1 /* term has been set */
66#define SG_CTERM 2 /* cterm has been set */
67#define SG_GUI 4 /* gui has been set */
68#define SG_LINK 8 /* link has been set */
69
70static garray_T highlight_ga; /* highlight groups for 'highlight' option */
71
72#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
73
Bram Moolenaar2dfb3862011-04-02 15:12:50 +020074#define MAX_HL_ID 20000 /* maximum value for a highlight ID. */
75
Bram Moolenaar071d4272004-06-13 20:20:40 +000076#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000077/* Flags to indicate an additional string for highlight name completion. */
78static int include_none = 0; /* when 1 include "None" */
79static int include_default = 0; /* when 1 include "default" */
80static int include_link = 0; /* when 2 include "link" and "clear" */
Bram Moolenaar071d4272004-06-13 20:20:40 +000081#endif
82
83/*
84 * The "term", "cterm" and "gui" arguments can be any combination of the
85 * following names, separated by commas (but no spaces!).
86 */
87static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000088 {"bold", "standout", "underline", "undercurl",
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +020089 "italic", "reverse", "inverse", "nocombine", "strikethrough", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000090static int hl_attr_table[] =
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +020091 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, HL_STRIKETHROUGH, 0};
Bram Moolenaar0cd2a942017-08-12 15:12:30 +020092#define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? attr_b : (attr_a)) | (attr_b))
Bram Moolenaar071d4272004-06-13 20:20:40 +000093
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010094static void syn_unadd_group(void);
95static void set_hl_attr(int idx);
96static void highlight_list_one(int id);
97static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
98static int syn_add_group(char_u *name);
99static int syn_list_header(int did_header, int outlen, int id);
100static int hl_has_settings(int idx, int check_link);
101static void highlight_clear(int idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000102
Bram Moolenaar61be73b2016-04-29 22:59:22 +0200103#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100104static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
Bram Moolenaar8a633e32016-04-21 21:10:14 +0200105#endif
106#ifdef FEAT_GUI
107static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100108static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000109#endif
110
111/*
112 * An attribute number is the index in attr_table plus ATTR_OFF.
113 */
114#define ATTR_OFF (HL_ALL + 1)
115
116#if defined(FEAT_SYN_HL) || defined(PROTO)
117
118#define SYN_NAMELEN 50 /* maximum length of a syntax name */
119
120/* different types of offsets that are possible */
121#define SPO_MS_OFF 0 /* match start offset */
122#define SPO_ME_OFF 1 /* match end offset */
123#define SPO_HS_OFF 2 /* highl. start offset */
124#define SPO_HE_OFF 3 /* highl. end offset */
125#define SPO_RS_OFF 4 /* region start offset */
126#define SPO_RE_OFF 5 /* region end offset */
127#define SPO_LC_OFF 6 /* leading context offset */
128#define SPO_COUNT 7
129
130static char *(spo_name_tab[SPO_COUNT]) =
131 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
132
133/*
134 * The patterns that are being searched for are stored in a syn_pattern.
135 * A match item consists of one pattern.
136 * A start/end item consists of n start patterns and m end patterns.
137 * A start/skip/end item consists of n start patterns, one skip pattern and m
138 * end patterns.
139 * For the latter two, the patterns are always consecutive: start-skip-end.
140 *
141 * A character offset can be given for the matched text (_m_start and _m_end)
142 * and for the actually highlighted text (_h_start and _h_end).
Bram Moolenaar36f92302018-02-24 21:36:34 +0100143 *
144 * Note that ordering of members is optimized to reduce padding.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000145 */
146typedef struct syn_pattern
147{
148 char sp_type; /* see SPTYPE_ defines below */
149 char sp_syncing; /* this item used for syncing */
Bram Moolenaar36f92302018-02-24 21:36:34 +0100150 short sp_syn_match_id; /* highlight group ID of pattern */
151 short sp_off_flags; /* see below */
152 int sp_offsets[SPO_COUNT]; /* offsets */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200153 int sp_flags; /* see HL_ defines below */
154#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200155 int sp_cchar; /* conceal substitute character */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200156#endif
Bram Moolenaar36f92302018-02-24 21:36:34 +0100157 int sp_ic; /* ignore-case flag for sp_prog */
158 int sp_sync_idx; /* sync item index (syncing only) */
159 int sp_line_id; /* ID of last line where tried */
160 int sp_startcol; /* next match in sp_line_id line */
161 short *sp_cont_list; /* cont. group IDs, if non-zero */
162 short *sp_next_list; /* next group IDs, if non-zero */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000163 struct sp_syn sp_syn; /* struct passed to in_id_list() */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000164 char_u *sp_pattern; /* regexp to match, pattern */
165 regprog_T *sp_prog; /* regexp to match, program */
Bram Moolenaarf7512552013-06-06 14:55:19 +0200166#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200167 syn_time_T sp_time;
168#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000169} synpat_T;
170
171/* The sp_off_flags are computed like this:
172 * offset from the start of the matched text: (1 << SPO_XX_OFF)
173 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
174 * When both are present, only one is used.
175 */
176
177#define SPTYPE_MATCH 1 /* match keyword with this group ID */
178#define SPTYPE_START 2 /* match a regexp, start of item */
179#define SPTYPE_END 3 /* match a regexp, end of item */
180#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
181
Bram Moolenaar071d4272004-06-13 20:20:40 +0000182
183#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
184
185#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
186
187/*
188 * Flags for b_syn_sync_flags:
189 */
190#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
191#define SF_MATCH 0x02 /* sync by matching a pattern */
192
193#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
194
Bram Moolenaar071d4272004-06-13 20:20:40 +0000195#define MAXKEYWLEN 80 /* maximum length of a keyword */
196
197/*
198 * The attributes of the syntax item that has been recognized.
199 */
200static int current_attr = 0; /* attr of current syntax word */
201#ifdef FEAT_EVAL
202static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000203static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000204#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200205#ifdef FEAT_CONCEAL
206static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200207static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200208static int current_sub_char = 0;
209#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000210
Bram Moolenaar217ad922005-03-20 22:37:15 +0000211typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000212{
213 char_u *scl_name; /* syntax cluster name */
214 char_u *scl_name_u; /* uppercase of scl_name */
215 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000216} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000217
218/*
219 * Methods of combining two clusters
220 */
221#define CLUSTER_REPLACE 1 /* replace first list with second */
222#define CLUSTER_ADD 2 /* add second list to first */
223#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
224
Bram Moolenaar217ad922005-03-20 22:37:15 +0000225#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000226
227/*
228 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200229 * 0 - 19999 normal syntax groups
230 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
231 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
232 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
233 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000234 */
Bram Moolenaar2dfb3862011-04-02 15:12:50 +0200235#define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */
Bram Moolenaar42431a72011-04-01 14:44:59 +0200236#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */
237#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */
238#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */
239
Bram Moolenaar42431a72011-04-01 14:44:59 +0200240#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */
241#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000242
243/*
244 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
245 * expand_filename(). Most of the other syntax commands don't need it, so
246 * instead of passing it to them, we stow it here.
247 */
248static char_u **syn_cmdlinep;
249
250/*
251 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200252 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000253 * rules in each ":syn include"'d file.
254 */
255static int current_syn_inc_tag = 0;
256static int running_syn_inc_tag = 0;
257
258/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000259 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
260 * This avoids adding a pointer to the hashtable item.
261 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
262 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
263 * HI2KE() converts a hashitem pointer to a var pointer.
264 */
265static keyentry_T dumkey;
266#define KE2HIKEY(kp) ((kp)->keyword)
267#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
268#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
269
270/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000271 * To reduce the time spent in keepend(), remember at which level in the state
272 * stack the first item with "keepend" is present. When "-1", there is no
273 * "keepend" on the stack.
274 */
275static int keepend_level = -1;
276
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200277static char msg_no_items[] = N_("No Syntax items defined for this buffer");
278
Bram Moolenaar071d4272004-06-13 20:20:40 +0000279/*
280 * For the current state we need to remember more than just the idx.
281 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
282 * (The end positions have the column number of the next char)
283 */
284typedef struct state_item
285{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000286 int si_idx; /* index of syntax pattern or
287 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000288 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000289 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000290 int si_m_lnum; /* lnum of the match */
291 int si_m_startcol; /* starting column of the match */
292 lpos_T si_m_endpos; /* just after end posn of the match */
293 lpos_T si_h_startpos; /* start position of the highlighting */
294 lpos_T si_h_endpos; /* end position of the highlighting */
295 lpos_T si_eoe_pos; /* end position of end pattern */
296 int si_end_idx; /* group ID for end pattern or zero */
297 int si_ends; /* if match ends before si_m_endpos */
298 int si_attr; /* attributes in this state */
299 long si_flags; /* HL_HAS_EOL flag in this state, and
300 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200301#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200302 int si_seqnr; /* sequence number */
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200303 int si_cchar; /* substitution character for conceal */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200304#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000305 short *si_cont_list; /* list of contained groups */
306 short *si_next_list; /* nextgroup IDs after this item ends */
307 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
308 * pattern */
309} stateitem_T;
310
311#define KEYWORD_IDX -1 /* value of si_idx for keywords */
312#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
313 but contained groups */
314
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200315#ifdef FEAT_CONCEAL
Bram Moolenaare7154eb2015-03-21 21:46:13 +0100316static int next_seqnr = 1; /* value to use for si_seqnr */
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200317#endif
318
Bram Moolenaar071d4272004-06-13 20:20:40 +0000319/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000320 * Struct to reduce the number of arguments to get_syn_options(), it's used
321 * very often.
322 */
323typedef struct
324{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000325 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000326 int keyword; /* TRUE for ":syn keyword" */
327 int *sync_idx; /* syntax item for "grouphere" argument, NULL
328 if not allowed */
329 char has_cont_list; /* TRUE if "cont_list" can be used */
330 short *cont_list; /* group IDs for "contains" argument */
331 short *cont_in_list; /* group IDs for "containedin" argument */
332 short *next_list; /* group IDs for "nextgroup" argument */
333} syn_opt_arg_T;
334
335/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000336 * The next possible match in the current line for any pattern is remembered,
337 * to avoid having to try for a match in each column.
338 * If next_match_idx == -1, not tried (in this line) yet.
339 * If next_match_col == MAXCOL, no match found in this line.
340 * (All end positions have the column of the char after the end)
341 */
342static int next_match_col; /* column for start of next match */
343static lpos_T next_match_m_endpos; /* position for end of next match */
344static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
345static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
346static int next_match_idx; /* index of matched item */
347static long next_match_flags; /* flags for next match */
348static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
349static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
350static int next_match_end_idx; /* ID of group for end pattn or zero */
351static reg_extmatch_T *next_match_extmatch = NULL;
352
353/*
354 * A state stack is an array of integers or stateitem_T, stored in a
Bram Moolenaarf86db782018-10-25 13:31:37 +0200355 * garray_T. A state stack is invalid if its itemsize entry is zero.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000356 */
357#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
358#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
359
360/*
361 * The current state (within the line) of the recognition engine.
362 * When current_state.ga_itemsize is 0 the current state is invalid.
363 */
364static win_T *syn_win; /* current window for highlighting */
365static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200366static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200367#ifdef FEAT_RELTIME
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200368static proftime_T *syn_tm; /* timeout limit */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200369#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000370static linenr_T current_lnum = 0; /* lnum of current state */
371static colnr_T current_col = 0; /* column of current state */
372static int current_state_stored = 0; /* TRUE if stored current state
373 * after setting current_finished */
374static int current_finished = 0; /* current line has been finished */
375static garray_T current_state /* current stack of state_items */
376 = {0, 0, 0, 0, NULL};
377static short *current_next_list = NULL; /* when non-zero, nextgroup list */
378static int current_next_flags = 0; /* flags for current_next_list */
379static int current_line_id = 0; /* unique number for current line */
380
381#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
382
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100383static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100384static int syn_match_linecont(linenr_T lnum);
385static void syn_start_line(void);
386static void syn_update_ends(int startofline);
387static void syn_stack_alloc(void);
388static int syn_stack_cleanup(void);
389static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
390static synstate_T *syn_stack_find_entry(linenr_T lnum);
391static synstate_T *store_current_state(void);
392static void load_current_state(synstate_T *from);
393static void invalidate_current_state(void);
394static int syn_stack_equal(synstate_T *sp);
395static void validate_current_state(void);
396static int syn_finish_line(int syncing);
397static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
398static int did_match_already(int idx, garray_T *gap);
399static stateitem_T *push_next_match(stateitem_T *cur_si);
400static void check_state_ends(void);
401static void update_si_attr(int idx);
402static void check_keepend(void);
403static void update_si_end(stateitem_T *sip, int startcol, int force);
404static short *copy_id_list(short *list);
405static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
406static int push_current_state(int idx);
407static void pop_current_state(void);
Bram Moolenaarf7512552013-06-06 14:55:19 +0200408#ifdef FEAT_PROFILE
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100409static void syn_clear_time(syn_time_T *tt);
410static void syntime_clear(void);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100411static void syntime_report(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200412static int syn_time_on = FALSE;
413# define IF_SYN_TIME(p) (p)
414#else
415# define IF_SYN_TIME(p) NULL
416typedef int syn_time_T;
417#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000418
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100419static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
420static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_endpos, long *flagsp, lpos_T *end_endpos, int *end_idx, reg_extmatch_T *start_ext);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000421
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100422static void limit_pos(lpos_T *pos, lpos_T *limit);
423static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
424static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
425static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
426static char_u *syn_getcurline(void);
427static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
428static int check_keyword_id(char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100429static void syn_remove_pattern(synblock_T *block, int idx);
430static void syn_clear_pattern(synblock_T *block, int i);
431static void syn_clear_cluster(synblock_T *block, int i);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100432static void syn_clear_one(int id, int syncing);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100433static void syn_cmd_onoff(exarg_T *eap, char *name);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100434static void syn_lines_msg(void);
435static void syn_match_msg(void);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100436static void syn_list_one(int id, int syncing, int link_only);
437static void syn_list_cluster(int id);
438static void put_id_list(char_u *name, short *list, int attr);
439static void put_pattern(char *s, int c, synpat_T *spp, int attr);
440static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
441static void syn_clear_keyword(int id, hashtab_T *ht);
442static void clear_keywtab(hashtab_T *ht);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100443static int syn_scl_namen2id(char_u *linep, int len);
444static int syn_check_cluster(char_u *pp, int len);
445static int syn_add_cluster(char_u *name);
446static void init_syn_patterns(void);
447static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100448static int get_id_list(char_u **arg, int keylen, short **list, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100449static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000450
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200451#if defined(FEAT_RELTIME) || defined(PROTO)
452/*
453 * Set the timeout used for syntax highlighting.
454 * Use NULL to reset, no timeout.
455 */
456 void
457syn_set_timeout(proftime_T *tm)
458{
459 syn_tm = tm;
460}
461#endif
462
Bram Moolenaar071d4272004-06-13 20:20:40 +0000463/*
464 * Start the syntax recognition for a line. This function is normally called
465 * from the screen updating, once for each displayed line.
466 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
467 * it. Careful: curbuf and curwin are likely to point to another buffer and
468 * window.
469 */
470 void
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200471syntax_start(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000472{
473 synstate_T *p;
474 synstate_T *last_valid = NULL;
475 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000476 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000477 linenr_T parsed_lnum;
478 linenr_T first_stored;
479 int dist;
Bram Moolenaar79518e22017-02-17 16:31:35 +0100480 static varnumber_T changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000481
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200482#ifdef FEAT_CONCEAL
483 current_sub_char = NUL;
484#endif
485
Bram Moolenaar071d4272004-06-13 20:20:40 +0000486 /*
487 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000488 * Also do this when a change was made, the current state may be invalid
489 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000490 */
Bram Moolenaarb681be12016-03-31 23:02:16 +0200491 if (syn_block != wp->w_s
492 || syn_buf != wp->w_buffer
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100493 || changedtick != CHANGEDTICK(syn_buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000494 {
495 invalidate_current_state();
496 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200497 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000498 }
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100499 changedtick = CHANGEDTICK(syn_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000500 syn_win = wp;
501
502 /*
503 * Allocate syntax stack when needed.
504 */
505 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200506 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000507 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200508 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000509
510 /*
511 * If the state of the end of the previous line is useful, store it.
512 */
513 if (VALID_STATE(&current_state)
514 && current_lnum < lnum
515 && current_lnum < syn_buf->b_ml.ml_line_count)
516 {
517 (void)syn_finish_line(FALSE);
518 if (!current_state_stored)
519 {
520 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000521 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000522 }
523
524 /*
525 * If the current_lnum is now the same as "lnum", keep the current
526 * state (this happens very often!). Otherwise invalidate
527 * current_state and figure it out below.
528 */
529 if (current_lnum != lnum)
530 invalidate_current_state();
531 }
532 else
533 invalidate_current_state();
534
535 /*
536 * Try to synchronize from a saved state in b_sst_array[].
537 * Only do this if lnum is not before and not to far beyond a saved state.
538 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200539 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000540 {
541 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200542 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000543 {
544 if (p->sst_lnum > lnum)
545 break;
546 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
547 {
548 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200549 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000550 last_min_valid = p;
551 }
552 }
553 if (last_min_valid != NULL)
554 load_current_state(last_min_valid);
555 }
556
557 /*
558 * If "lnum" is before or far beyond a line with a saved state, need to
559 * re-synchronize.
560 */
561 if (INVALID_STATE(&current_state))
562 {
563 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200564 if (current_lnum == 1)
565 /* First line is always valid, no matter "minlines". */
566 first_stored = 1;
567 else
568 /* Need to parse "minlines" lines before state can be considered
569 * valid to store. */
570 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000571 }
572 else
573 first_stored = current_lnum;
574
575 /*
576 * Advance from the sync point or saved state until the current line.
577 * Save some entries for syncing with later on.
578 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200579 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000580 dist = 999999;
581 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200582 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000583 while (current_lnum < lnum)
584 {
585 syn_start_line();
586 (void)syn_finish_line(FALSE);
587 ++current_lnum;
588
589 /* If we parsed at least "minlines" lines or started at a valid
590 * state, the current state is considered valid. */
591 if (current_lnum >= first_stored)
592 {
593 /* Check if the saved state entry is for the current line and is
594 * equal to the current state. If so, then validate all saved
595 * states that depended on a change before the parsed line. */
596 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000597 prev = syn_stack_find_entry(current_lnum - 1);
598 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200599 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000600 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000601 sp = prev;
602 while (sp != NULL && sp->sst_lnum < current_lnum)
603 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000604 if (sp != NULL
605 && sp->sst_lnum == current_lnum
606 && syn_stack_equal(sp))
607 {
608 parsed_lnum = current_lnum;
609 prev = sp;
610 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
611 {
612 if (sp->sst_lnum <= lnum)
613 /* valid state before desired line, use this one */
614 prev = sp;
615 else if (sp->sst_change_lnum == 0)
616 /* past saved states depending on change, break here. */
617 break;
618 sp->sst_change_lnum = 0;
619 sp = sp->sst_next;
620 }
621 load_current_state(prev);
622 }
623 /* Store the state at this line when it's the first one, the line
624 * where we start parsing, or some distance from the previously
625 * saved state. But only when parsed at least 'minlines'. */
626 else if (prev == NULL
627 || current_lnum == lnum
628 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000629 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000630 }
631
632 /* This can take a long time: break when CTRL-C pressed. The current
633 * state will be wrong then. */
634 line_breakcheck();
635 if (got_int)
636 {
637 current_lnum = lnum;
638 break;
639 }
640 }
641
642 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000643}
644
645/*
646 * We cannot simply discard growarrays full of state_items or buf_states; we
647 * have to manually release their extmatch pointers first.
648 */
649 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100650clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000651{
652 int i;
653 garray_T *gap;
654
655 if (p->sst_stacksize > SST_FIX_STATES)
656 {
657 gap = &(p->sst_union.sst_ga);
658 for (i = 0; i < gap->ga_len; i++)
659 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
660 ga_clear(gap);
661 }
662 else
663 {
664 for (i = 0; i < p->sst_stacksize; i++)
665 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
666 }
667}
668
669/*
670 * Cleanup the current_state stack.
671 */
672 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100673clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000674{
675 int i;
676 stateitem_T *sip;
677
678 sip = (stateitem_T *)(current_state.ga_data);
679 for (i = 0; i < current_state.ga_len; i++)
680 unref_extmatch(sip[i].si_extmatch);
681 ga_clear(&current_state);
682}
683
684/*
685 * Try to find a synchronisation point for line "lnum".
686 *
687 * This sets current_lnum and the current state. One of three methods is
688 * used:
689 * 1. Search backwards for the end of a C-comment.
690 * 2. Search backwards for given sync patterns.
691 * 3. Simply start on a given number of lines above "lnum".
692 */
693 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100694syn_sync(
695 win_T *wp,
696 linenr_T start_lnum,
697 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000698{
699 buf_T *curbuf_save;
700 win_T *curwin_save;
701 pos_T cursor_save;
702 int idx;
703 linenr_T lnum;
704 linenr_T end_lnum;
705 linenr_T break_lnum;
706 int had_sync_point;
707 stateitem_T *cur_si;
708 synpat_T *spp;
709 char_u *line;
710 int found_flags = 0;
711 int found_match_idx = 0;
712 linenr_T found_current_lnum = 0;
713 int found_current_col= 0;
714 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000715 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000716
717 /*
718 * Clear any current state that might be hanging around.
719 */
720 invalidate_current_state();
721
722 /*
723 * Start at least "minlines" back. Default starting point for parsing is
724 * there.
725 * Start further back, to avoid that scrolling backwards will result in
726 * resyncing for every line. Now it resyncs only one out of N lines,
727 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
728 * Watch out for overflow when minlines is MAXLNUM.
729 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200730 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000731 start_lnum = 1;
732 else
733 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200734 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000735 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200736 else if (syn_block->b_syn_sync_minlines < 10)
737 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000738 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200739 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
740 if (syn_block->b_syn_sync_maxlines != 0
741 && lnum > syn_block->b_syn_sync_maxlines)
742 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000743 if (lnum >= start_lnum)
744 start_lnum = 1;
745 else
746 start_lnum -= lnum;
747 }
748 current_lnum = start_lnum;
749
750 /*
751 * 1. Search backwards for the end of a C-style comment.
752 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200753 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000754 {
755 /* Need to make syn_buf the current buffer for a moment, to be able to
756 * use find_start_comment(). */
757 curwin_save = curwin;
758 curwin = wp;
759 curbuf_save = curbuf;
760 curbuf = syn_buf;
761
762 /*
763 * Skip lines that end in a backslash.
764 */
765 for ( ; start_lnum > 1; --start_lnum)
766 {
767 line = ml_get(start_lnum - 1);
768 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
769 break;
770 }
771 current_lnum = start_lnum;
772
773 /* set cursor to start of search */
774 cursor_save = wp->w_cursor;
775 wp->w_cursor.lnum = start_lnum;
776 wp->w_cursor.col = 0;
777
778 /*
779 * If the line is inside a comment, need to find the syntax item that
780 * defines the comment.
781 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
782 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200783 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000784 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200785 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
786 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
787 == syn_block->b_syn_sync_id
788 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000789 {
790 validate_current_state();
791 if (push_current_state(idx) == OK)
792 update_si_attr(current_state.ga_len - 1);
793 break;
794 }
795 }
796
797 /* restore cursor and buffer */
798 wp->w_cursor = cursor_save;
799 curwin = curwin_save;
800 curbuf = curbuf_save;
801 }
802
803 /*
804 * 2. Search backwards for given sync patterns.
805 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200806 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000807 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200808 if (syn_block->b_syn_sync_maxlines != 0
809 && start_lnum > syn_block->b_syn_sync_maxlines)
810 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000811 else
812 break_lnum = 0;
813
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000814 found_m_endpos.lnum = 0;
815 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000816 end_lnum = start_lnum;
817 lnum = start_lnum;
818 while (--lnum > break_lnum)
819 {
820 /* This can take a long time: break when CTRL-C pressed. */
821 line_breakcheck();
822 if (got_int)
823 {
824 invalidate_current_state();
825 current_lnum = start_lnum;
826 break;
827 }
828
829 /* Check if we have run into a valid saved state stack now. */
830 if (last_valid != NULL && lnum == last_valid->sst_lnum)
831 {
832 load_current_state(last_valid);
833 break;
834 }
835
836 /*
837 * Check if the previous line has the line-continuation pattern.
838 */
839 if (lnum > 1 && syn_match_linecont(lnum - 1))
840 continue;
841
842 /*
843 * Start with nothing on the state stack
844 */
845 validate_current_state();
846
847 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
848 {
849 syn_start_line();
850 for (;;)
851 {
852 had_sync_point = syn_finish_line(TRUE);
853 /*
854 * When a sync point has been found, remember where, and
855 * continue to look for another one, further on in the line.
856 */
857 if (had_sync_point && current_state.ga_len)
858 {
859 cur_si = &CUR_STATE(current_state.ga_len - 1);
860 if (cur_si->si_m_endpos.lnum > start_lnum)
861 {
862 /* ignore match that goes to after where started */
863 current_lnum = end_lnum;
864 break;
865 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000866 if (cur_si->si_idx < 0)
867 {
868 /* Cannot happen? */
869 found_flags = 0;
870 found_match_idx = KEYWORD_IDX;
871 }
872 else
873 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200874 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000875 found_flags = spp->sp_flags;
876 found_match_idx = spp->sp_sync_idx;
877 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000878 found_current_lnum = current_lnum;
879 found_current_col = current_col;
880 found_m_endpos = cur_si->si_m_endpos;
881 /*
882 * Continue after the match (be aware of a zero-length
883 * match).
884 */
885 if (found_m_endpos.lnum > current_lnum)
886 {
887 current_lnum = found_m_endpos.lnum;
888 current_col = found_m_endpos.col;
889 if (current_lnum >= end_lnum)
890 break;
891 }
892 else if (found_m_endpos.col > current_col)
893 current_col = found_m_endpos.col;
894 else
895 ++current_col;
896
897 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000898 * an item that ends here, need to do that now. Be
899 * careful not to go past the NUL. */
900 prev_current_col = current_col;
901 if (syn_getcurline()[current_col] != NUL)
902 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000903 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000904 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000905 }
906 else
907 break;
908 }
909 }
910
911 /*
912 * If a sync point was encountered, break here.
913 */
914 if (found_flags)
915 {
916 /*
917 * Put the item that was specified by the sync point on the
918 * state stack. If there was no item specified, make the
919 * state stack empty.
920 */
921 clear_current_state();
922 if (found_match_idx >= 0
923 && push_current_state(found_match_idx) == OK)
924 update_si_attr(current_state.ga_len - 1);
925
926 /*
927 * When using "grouphere", continue from the sync point
928 * match, until the end of the line. Parsing starts at
929 * the next line.
930 * For "groupthere" the parsing starts at start_lnum.
931 */
932 if (found_flags & HL_SYNC_HERE)
933 {
934 if (current_state.ga_len)
935 {
936 cur_si = &CUR_STATE(current_state.ga_len - 1);
937 cur_si->si_h_startpos.lnum = found_current_lnum;
938 cur_si->si_h_startpos.col = found_current_col;
939 update_si_end(cur_si, (int)current_col, TRUE);
940 check_keepend();
941 }
942 current_col = found_m_endpos.col;
943 current_lnum = found_m_endpos.lnum;
944 (void)syn_finish_line(FALSE);
945 ++current_lnum;
946 }
947 else
948 current_lnum = start_lnum;
949
950 break;
951 }
952
953 end_lnum = lnum;
954 invalidate_current_state();
955 }
956
957 /* Ran into start of the file or exceeded maximum number of lines */
958 if (lnum <= break_lnum)
959 {
960 invalidate_current_state();
961 current_lnum = break_lnum + 1;
962 }
963 }
964
965 validate_current_state();
966}
967
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100968 static void
969save_chartab(char_u *chartab)
970{
971 if (syn_block->b_syn_isk != empty_option)
972 {
973 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
974 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
975 (size_t)32);
976 }
977}
978
979 static void
980restore_chartab(char_u *chartab)
981{
982 if (syn_win->w_s->b_syn_isk != empty_option)
983 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
984}
985
Bram Moolenaar071d4272004-06-13 20:20:40 +0000986/*
987 * Return TRUE if the line-continuation pattern matches in line "lnum".
988 */
989 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100990syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000991{
992 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100993 int r;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100994 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000995
Bram Moolenaar860cae12010-06-05 23:22:07 +0200996 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000997 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100998 /* use syntax iskeyword option */
999 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001000 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
1001 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001002 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001003 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001004 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001005 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001006 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001007 }
1008 return FALSE;
1009}
1010
1011/*
1012 * Prepare the current state for the start of a line.
1013 */
1014 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001015syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001016{
1017 current_finished = FALSE;
1018 current_col = 0;
1019
1020 /*
1021 * Need to update the end of a start/skip/end that continues from the
1022 * previous line and regions that have "keepend".
1023 */
1024 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001025 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001026 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001027 check_state_ends();
1028 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001029
1030 next_match_idx = -1;
1031 ++current_line_id;
Bram Moolenaarea20de82017-06-24 22:52:24 +02001032#ifdef FEAT_CONCEAL
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001033 next_seqnr = 1;
Bram Moolenaarea20de82017-06-24 22:52:24 +02001034#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001035}
1036
1037/*
1038 * Check for items in the stack that need their end updated.
1039 * When "startofline" is TRUE the last item is always updated.
1040 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1041 */
1042 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001043syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001044{
1045 stateitem_T *cur_si;
1046 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001047 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001048
1049 if (startofline)
1050 {
1051 /* Check for a match carried over from a previous line with a
1052 * contained region. The match ends as soon as the region ends. */
1053 for (i = 0; i < current_state.ga_len; ++i)
1054 {
1055 cur_si = &CUR_STATE(i);
1056 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001057 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001058 == SPTYPE_MATCH
1059 && cur_si->si_m_endpos.lnum < current_lnum)
1060 {
1061 cur_si->si_flags |= HL_MATCHCONT;
1062 cur_si->si_m_endpos.lnum = 0;
1063 cur_si->si_m_endpos.col = 0;
1064 cur_si->si_h_endpos = cur_si->si_m_endpos;
1065 cur_si->si_ends = TRUE;
1066 }
1067 }
1068 }
1069
1070 /*
1071 * Need to update the end of a start/skip/end that continues from the
1072 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001073 * influence contained items. If we've just removed "extend"
1074 * (startofline == 0) then we should update ends of normal regions
1075 * contained inside "keepend" because "extend" could have extended
1076 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001077 * Then check for items ending in column 0.
1078 */
1079 i = current_state.ga_len - 1;
1080 if (keepend_level >= 0)
1081 for ( ; i > keepend_level; --i)
1082 if (CUR_STATE(i).si_flags & HL_EXTEND)
1083 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001084
1085 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001086 for ( ; i < current_state.ga_len; ++i)
1087 {
1088 cur_si = &CUR_STATE(i);
1089 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001090 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001091 || (i == current_state.ga_len - 1 && startofline))
1092 {
1093 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1094 cur_si->si_h_startpos.lnum = current_lnum;
1095
1096 if (!(cur_si->si_flags & HL_MATCHCONT))
1097 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001098
1099 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1100 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001101 }
1102 }
1103 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001104}
1105
1106/****************************************
1107 * Handling of the state stack cache.
1108 */
1109
1110/*
1111 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1112 *
1113 * To speed up syntax highlighting, the state stack for the start of some
1114 * lines is cached. These entries can be used to start parsing at that point.
1115 *
1116 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1117 * valid entries. b_sst_first points to the first one, then follow sst_next.
1118 * The entries are sorted on line number. The first entry is often for line 2
1119 * (line 1 always starts with an empty stack).
1120 * There is also a list for free entries. This construction is used to avoid
1121 * having to allocate and free memory blocks too often.
1122 *
1123 * When making changes to the buffer, this is logged in b_mod_*. When calling
1124 * update_screen() to update the display, it will call
1125 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1126 * entries. The entries which are inside the changed area are removed,
1127 * because they must be recomputed. Entries below the changed have their line
1128 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1129 * set to indicate that a check must be made if the changed lines would change
1130 * the cached entry.
1131 *
1132 * When later displaying lines, an entry is stored for each line. Displayed
1133 * lines are likely to be displayed again, in which case the state at the
1134 * start of the line is needed.
1135 * For not displayed lines, an entry is stored for every so many lines. These
1136 * entries will be used e.g., when scrolling backwards. The distance between
1137 * entries depends on the number of lines in the buffer. For small buffers
1138 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1139 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1140 */
1141
Bram Moolenaar860cae12010-06-05 23:22:07 +02001142 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001143syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001144{
1145 synstate_T *p;
1146
1147 if (block->b_sst_array != NULL)
1148 {
1149 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1150 clear_syn_state(p);
Bram Moolenaard23a8232018-02-10 18:45:26 +01001151 VIM_CLEAR(block->b_sst_array);
Bram Moolenaar95892c22018-09-28 22:26:54 +02001152 block->b_sst_first = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001153 block->b_sst_len = 0;
1154 }
1155}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001156/*
1157 * Free b_sst_array[] for buffer "buf".
1158 * Used when syntax items changed to force resyncing everywhere.
1159 */
1160 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001161syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001162{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001163#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001164 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001165#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001166
Bram Moolenaar860cae12010-06-05 23:22:07 +02001167 syn_stack_free_block(block);
1168
Bram Moolenaar071d4272004-06-13 20:20:40 +00001169#ifdef FEAT_FOLDING
1170 /* When using "syntax" fold method, must update all folds. */
1171 FOR_ALL_WINDOWS(wp)
1172 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001173 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001174 foldUpdateAll(wp);
1175 }
1176#endif
1177}
1178
1179/*
1180 * Allocate the syntax state stack for syn_buf when needed.
1181 * If the number of entries in b_sst_array[] is much too big or a bit too
1182 * small, reallocate it.
1183 * Also used to allocate b_sst_array[] for the first time.
1184 */
1185 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001186syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001187{
1188 long len;
1189 synstate_T *to, *from;
1190 synstate_T *sstp;
1191
1192 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1193 if (len < SST_MIN_ENTRIES)
1194 len = SST_MIN_ENTRIES;
1195 else if (len > SST_MAX_ENTRIES)
1196 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001197 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001198 {
1199 /* Allocate 50% too much, to avoid reallocating too often. */
1200 len = syn_buf->b_ml.ml_line_count;
1201 len = (len + len / 2) / SST_DIST + Rows * 2;
1202 if (len < SST_MIN_ENTRIES)
1203 len = SST_MIN_ENTRIES;
1204 else if (len > SST_MAX_ENTRIES)
1205 len = SST_MAX_ENTRIES;
1206
Bram Moolenaar860cae12010-06-05 23:22:07 +02001207 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001208 {
1209 /* When shrinking the array, cleanup the existing stack.
1210 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001211 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001212 && syn_stack_cleanup())
1213 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001214 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1215 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001216 }
1217
1218 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1219 if (sstp == NULL) /* out of memory! */
1220 return;
1221
1222 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001223 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001224 {
1225 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001226 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001227 from = from->sst_next)
1228 {
1229 ++to;
1230 *to = *from;
1231 to->sst_next = to + 1;
1232 }
1233 }
1234 if (to != sstp - 1)
1235 {
1236 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001237 syn_block->b_sst_first = sstp;
1238 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001239 }
1240 else
1241 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001242 syn_block->b_sst_first = NULL;
1243 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244 }
1245
1246 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001247 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001248 while (++to < sstp + len)
1249 to->sst_next = to + 1;
1250 (sstp + len - 1)->sst_next = NULL;
1251
Bram Moolenaar860cae12010-06-05 23:22:07 +02001252 vim_free(syn_block->b_sst_array);
1253 syn_block->b_sst_array = sstp;
1254 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001255 }
1256}
1257
1258/*
1259 * Check for changes in a buffer to affect stored syntax states. Uses the
1260 * b_mod_* fields.
1261 * Called from update_screen(), before screen is being updated, once for each
1262 * displayed buffer.
1263 */
1264 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001265syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001266{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001267 win_T *wp;
1268
1269 syn_stack_apply_changes_block(&buf->b_s, buf);
1270
1271 FOR_ALL_WINDOWS(wp)
1272 {
1273 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1274 syn_stack_apply_changes_block(wp->w_s, buf);
1275 }
1276}
1277
1278 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001279syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001280{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001281 synstate_T *p, *prev, *np;
1282 linenr_T n;
1283
Bram Moolenaar071d4272004-06-13 20:20:40 +00001284 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001285 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001286 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001287 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001288 {
1289 n = p->sst_lnum + buf->b_mod_xlines;
1290 if (n <= buf->b_mod_bot)
1291 {
1292 /* this state is inside the changed area, remove it */
1293 np = p->sst_next;
1294 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001295 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001296 else
1297 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001298 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001299 p = np;
1300 continue;
1301 }
1302 /* This state is below the changed area. Remember the line
1303 * that needs to be parsed before this entry can be made valid
1304 * again. */
1305 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1306 {
1307 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1308 p->sst_change_lnum += buf->b_mod_xlines;
1309 else
1310 p->sst_change_lnum = buf->b_mod_top;
1311 }
1312 if (p->sst_change_lnum == 0
1313 || p->sst_change_lnum < buf->b_mod_bot)
1314 p->sst_change_lnum = buf->b_mod_bot;
1315
1316 p->sst_lnum = n;
1317 }
1318 prev = p;
1319 p = p->sst_next;
1320 }
1321}
1322
1323/*
1324 * Reduce the number of entries in the state stack for syn_buf.
1325 * Returns TRUE if at least one entry was freed.
1326 */
1327 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001328syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001329{
1330 synstate_T *p, *prev;
1331 disptick_T tick;
1332 int above;
1333 int dist;
1334 int retval = FALSE;
1335
Bram Moolenaar95892c22018-09-28 22:26:54 +02001336 if (syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001337 return retval;
1338
1339 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001340 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001341 dist = 999999;
1342 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001343 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001344
1345 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001346 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001347 * be removed. Set "above" when the "tick" for the oldest entry is above
1348 * "b_sst_lasttick" (the display tick wraps around).
1349 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001350 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001351 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001352 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001353 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1354 {
1355 if (prev->sst_lnum + dist > p->sst_lnum)
1356 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001357 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001358 {
1359 if (!above || p->sst_tick < tick)
1360 tick = p->sst_tick;
1361 above = TRUE;
1362 }
1363 else if (!above && p->sst_tick < tick)
1364 tick = p->sst_tick;
1365 }
1366 }
1367
1368 /*
1369 * Go through the list to make the entries for the oldest tick at an
1370 * interval of several lines.
1371 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001372 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001373 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1374 {
1375 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1376 {
1377 /* Move this entry from used list to free list */
1378 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001379 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001380 p = prev;
1381 retval = TRUE;
1382 }
1383 }
1384 return retval;
1385}
1386
1387/*
1388 * Free the allocated memory for a syn_state item.
1389 * Move the entry into the free list.
1390 */
1391 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001392syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001393{
1394 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001395 p->sst_next = block->b_sst_firstfree;
1396 block->b_sst_firstfree = p;
1397 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001398}
1399
1400/*
1401 * Find an entry in the list of state stacks at or before "lnum".
1402 * Returns NULL when there is no entry or the first entry is after "lnum".
1403 */
1404 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001405syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001406{
1407 synstate_T *p, *prev;
1408
1409 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001410 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001411 {
1412 if (p->sst_lnum == lnum)
1413 return p;
1414 if (p->sst_lnum > lnum)
1415 break;
1416 }
1417 return prev;
1418}
1419
1420/*
1421 * Try saving the current state in b_sst_array[].
1422 * The current state must be valid for the start of the current_lnum line!
1423 */
1424 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001425store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001426{
1427 int i;
1428 synstate_T *p;
1429 bufstate_T *bp;
1430 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001431 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001432
1433 /*
1434 * If the current state contains a start or end pattern that continues
1435 * from the previous line, we can't use it. Don't store it then.
1436 */
1437 for (i = current_state.ga_len - 1; i >= 0; --i)
1438 {
1439 cur_si = &CUR_STATE(i);
1440 if (cur_si->si_h_startpos.lnum >= current_lnum
1441 || cur_si->si_m_endpos.lnum >= current_lnum
1442 || cur_si->si_h_endpos.lnum >= current_lnum
1443 || (cur_si->si_end_idx
1444 && cur_si->si_eoe_pos.lnum >= current_lnum))
1445 break;
1446 }
1447 if (i >= 0)
1448 {
1449 if (sp != NULL)
1450 {
1451 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001452 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001453 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001454 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001455 else
1456 {
1457 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001458 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001459 if (p->sst_next == sp)
1460 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001461 if (p != NULL) /* just in case */
1462 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001463 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001464 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001465 sp = NULL;
1466 }
1467 }
1468 else if (sp == NULL || sp->sst_lnum != current_lnum)
1469 {
1470 /*
1471 * Add a new entry
1472 */
1473 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001474 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001475 {
1476 (void)syn_stack_cleanup();
1477 /* "sp" may have been moved to the freelist now */
1478 sp = syn_stack_find_entry(current_lnum);
1479 }
1480 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001481 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001482 sp = NULL;
1483 else
1484 {
1485 /* Take the first item from the free list and put it in the used
1486 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001487 p = syn_block->b_sst_firstfree;
1488 syn_block->b_sst_firstfree = p->sst_next;
1489 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001490 if (sp == NULL)
1491 {
1492 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001493 p->sst_next = syn_block->b_sst_first;
1494 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001495 }
1496 else
1497 {
1498 /* insert in list after *sp */
1499 p->sst_next = sp->sst_next;
1500 sp->sst_next = p;
1501 }
1502 sp = p;
1503 sp->sst_stacksize = 0;
1504 sp->sst_lnum = current_lnum;
1505 }
1506 }
1507 if (sp != NULL)
1508 {
1509 /* When overwriting an existing state stack, clear it first */
1510 clear_syn_state(sp);
1511 sp->sst_stacksize = current_state.ga_len;
1512 if (current_state.ga_len > SST_FIX_STATES)
1513 {
1514 /* Need to clear it, might be something remaining from when the
1515 * length was less than SST_FIX_STATES. */
1516 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1517 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1518 sp->sst_stacksize = 0;
1519 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001520 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001521 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1522 }
1523 else
1524 bp = sp->sst_union.sst_stack;
1525 for (i = 0; i < sp->sst_stacksize; ++i)
1526 {
1527 bp[i].bs_idx = CUR_STATE(i).si_idx;
1528 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001529#ifdef FEAT_CONCEAL
1530 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1531 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1532#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001533 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1534 }
1535 sp->sst_next_flags = current_next_flags;
1536 sp->sst_next_list = current_next_list;
1537 sp->sst_tick = display_tick;
1538 sp->sst_change_lnum = 0;
1539 }
1540 current_state_stored = TRUE;
1541 return sp;
1542}
1543
1544/*
1545 * Copy a state stack from "from" in b_sst_array[] to current_state;
1546 */
1547 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001548load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001549{
1550 int i;
1551 bufstate_T *bp;
1552
1553 clear_current_state();
1554 validate_current_state();
1555 keepend_level = -1;
1556 if (from->sst_stacksize
1557 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1558 {
1559 if (from->sst_stacksize > SST_FIX_STATES)
1560 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1561 else
1562 bp = from->sst_union.sst_stack;
1563 for (i = 0; i < from->sst_stacksize; ++i)
1564 {
1565 CUR_STATE(i).si_idx = bp[i].bs_idx;
1566 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001567#ifdef FEAT_CONCEAL
1568 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1569 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1570#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001571 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1572 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1573 keepend_level = i;
1574 CUR_STATE(i).si_ends = FALSE;
1575 CUR_STATE(i).si_m_lnum = 0;
1576 if (CUR_STATE(i).si_idx >= 0)
1577 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001578 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001579 else
1580 CUR_STATE(i).si_next_list = NULL;
1581 update_si_attr(i);
1582 }
1583 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001584 }
1585 current_next_list = from->sst_next_list;
1586 current_next_flags = from->sst_next_flags;
1587 current_lnum = from->sst_lnum;
1588}
1589
1590/*
1591 * Compare saved state stack "*sp" with the current state.
1592 * Return TRUE when they are equal.
1593 */
1594 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001595syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001596{
1597 int i, j;
1598 bufstate_T *bp;
1599 reg_extmatch_T *six, *bsx;
1600
1601 /* First a quick check if the stacks have the same size end nextlist. */
1602 if (sp->sst_stacksize == current_state.ga_len
1603 && sp->sst_next_list == current_next_list)
1604 {
1605 /* Need to compare all states on both stacks. */
1606 if (sp->sst_stacksize > SST_FIX_STATES)
1607 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1608 else
1609 bp = sp->sst_union.sst_stack;
1610
1611 for (i = current_state.ga_len; --i >= 0; )
1612 {
1613 /* If the item has another index the state is different. */
1614 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1615 break;
1616 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1617 {
1618 /* When the extmatch pointers are different, the strings in
1619 * them can still be the same. Check if the extmatch
1620 * references are equal. */
1621 bsx = bp[i].bs_extmatch;
1622 six = CUR_STATE(i).si_extmatch;
1623 /* If one of the extmatch pointers is NULL the states are
1624 * different. */
1625 if (bsx == NULL || six == NULL)
1626 break;
1627 for (j = 0; j < NSUBEXP; ++j)
1628 {
1629 /* Check each referenced match string. They must all be
1630 * equal. */
1631 if (bsx->matches[j] != six->matches[j])
1632 {
1633 /* If the pointer is different it can still be the
1634 * same text. Compare the strings, ignore case when
1635 * the start item has the sp_ic flag set. */
1636 if (bsx->matches[j] == NULL
1637 || six->matches[j] == NULL)
1638 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001639 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001640 ? MB_STRICMP(bsx->matches[j],
1641 six->matches[j]) != 0
1642 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1643 break;
1644 }
1645 }
1646 if (j != NSUBEXP)
1647 break;
1648 }
1649 }
1650 if (i < 0)
1651 return TRUE;
1652 }
1653 return FALSE;
1654}
1655
1656/*
1657 * We stop parsing syntax above line "lnum". If the stored state at or below
1658 * this line depended on a change before it, it now depends on the line below
1659 * the last parsed line.
1660 * The window looks like this:
1661 * line which changed
1662 * displayed line
1663 * displayed line
1664 * lnum -> line below window
1665 */
1666 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001667syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001668{
1669 synstate_T *sp;
1670
1671 sp = syn_stack_find_entry(lnum);
1672 if (sp != NULL && sp->sst_lnum < lnum)
1673 sp = sp->sst_next;
1674
1675 if (sp != NULL && sp->sst_change_lnum != 0)
1676 sp->sst_change_lnum = lnum;
1677}
1678
1679/*
1680 * End of handling of the state stack.
1681 ****************************************/
1682
1683 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001684invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001685{
1686 clear_current_state();
1687 current_state.ga_itemsize = 0; /* mark current_state invalid */
1688 current_next_list = NULL;
1689 keepend_level = -1;
1690}
1691
1692 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001693validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001694{
1695 current_state.ga_itemsize = sizeof(stateitem_T);
1696 current_state.ga_growsize = 3;
1697}
1698
1699/*
1700 * Return TRUE if the syntax at start of lnum changed since last time.
1701 * This will only be called just after get_syntax_attr() for the previous
1702 * line, to check if the next line needs to be redrawn too.
1703 */
1704 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001705syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001706{
1707 int retval = TRUE;
1708 synstate_T *sp;
1709
Bram Moolenaar071d4272004-06-13 20:20:40 +00001710 /*
1711 * Check the state stack when:
1712 * - lnum is just below the previously syntaxed line.
1713 * - lnum is not before the lines with saved states.
1714 * - lnum is not past the lines with saved states.
1715 * - lnum is at or before the last changed line.
1716 */
1717 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1718 {
1719 sp = syn_stack_find_entry(lnum);
1720 if (sp != NULL && sp->sst_lnum == lnum)
1721 {
1722 /*
1723 * finish the previous line (needed when not all of the line was
1724 * drawn)
1725 */
1726 (void)syn_finish_line(FALSE);
1727
1728 /*
1729 * Compare the current state with the previously saved state of
1730 * the line.
1731 */
1732 if (syn_stack_equal(sp))
1733 retval = FALSE;
1734
1735 /*
1736 * Store the current state in b_sst_array[] for later use.
1737 */
1738 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001739 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001740 }
1741 }
1742
Bram Moolenaar071d4272004-06-13 20:20:40 +00001743 return retval;
1744}
1745
1746/*
1747 * Finish the current line.
1748 * This doesn't return any attributes, it only gets the state at the end of
1749 * the line. It can start anywhere in the line, as long as the current state
1750 * is valid.
1751 */
1752 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001753syn_finish_line(
1754 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001755{
1756 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001757 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001758
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001759 while (!current_finished)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001760 {
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001761 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1762 /*
1763 * When syncing, and found some item, need to check the item.
1764 */
1765 if (syncing && current_state.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001766 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001767 /*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001768 * Check for match with sync item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001769 */
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001770 cur_si = &CUR_STATE(current_state.ga_len - 1);
1771 if (cur_si->si_idx >= 0
1772 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1773 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1774 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001775
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001776 /* syn_current_attr() will have skipped the check for an item
1777 * that ends here, need to do that now. Be careful not to go
1778 * past the NUL. */
1779 prev_current_col = current_col;
1780 if (syn_getcurline()[current_col] != NUL)
1781 ++current_col;
1782 check_state_ends();
1783 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001784 }
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001785 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001786 }
1787 return FALSE;
1788}
1789
1790/*
1791 * Return highlight attributes for next character.
1792 * Must first call syntax_start() once for the line.
1793 * "col" is normally 0 for the first use in a line, and increments by one each
1794 * time. It's allowed to skip characters and to stop before the end of the
1795 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001796 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1797 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001798 */
1799 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001800get_syntax_attr(
1801 colnr_T col,
1802 int *can_spell,
1803 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001804{
1805 int attr = 0;
1806
Bram Moolenaar349955a2007-08-14 21:07:36 +00001807 if (can_spell != NULL)
1808 /* Default: Only do spelling when there is no @Spell cluster or when
1809 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001810 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1811 ? (syn_block->b_spell_cluster_id == 0)
1812 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001813
Bram Moolenaar071d4272004-06-13 20:20:40 +00001814 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001815 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001816 return 0;
1817
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001818 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001819 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001820 {
1821 clear_current_state();
1822#ifdef FEAT_EVAL
1823 current_id = 0;
1824 current_trans_id = 0;
1825#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001826#ifdef FEAT_CONCEAL
1827 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001828 current_seqnr = 0;
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001829#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001830 return 0;
1831 }
1832
Bram Moolenaar071d4272004-06-13 20:20:40 +00001833 /* Make sure current_state is valid */
1834 if (INVALID_STATE(&current_state))
1835 validate_current_state();
1836
1837 /*
1838 * Skip from the current column to "col", get the attributes for "col".
1839 */
1840 while (current_col <= col)
1841 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001842 attr = syn_current_attr(FALSE, TRUE, can_spell,
1843 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001844 ++current_col;
1845 }
1846
Bram Moolenaar071d4272004-06-13 20:20:40 +00001847 return attr;
1848}
1849
1850/*
1851 * Get syntax attributes for current_lnum, current_col.
1852 */
1853 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001854syn_current_attr(
1855 int syncing, /* When 1: called for syncing */
1856 int displaying, /* result will be displayed */
1857 int *can_spell, /* return: do spell checking */
1858 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001859{
1860 int syn_id;
1861 lpos_T endpos; /* was: char_u *endp; */
1862 lpos_T hl_startpos; /* was: int hl_startcol; */
1863 lpos_T hl_endpos;
1864 lpos_T eos_pos; /* end-of-start match (start region) */
1865 lpos_T eoe_pos; /* end-of-end pattern */
1866 int end_idx; /* group ID for end pattern */
1867 int idx;
1868 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001869 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001870 int startcol;
1871 int endcol;
1872 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001873 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001874 short *next_list;
1875 int found_match; /* found usable match */
1876 static int try_next_column = FALSE; /* must try in next col */
1877 int do_keywords;
1878 regmmatch_T regmatch;
1879 lpos_T pos;
1880 int lc_col;
1881 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001882 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001883 char_u *line; /* current line. NOTE: becomes invalid after
1884 looking for a pattern match! */
1885
1886 /* variables for zero-width matches that have a "nextgroup" argument */
1887 int keep_next_list;
1888 int zero_width_next_list = FALSE;
1889 garray_T zero_width_next_ga;
1890
1891 /*
1892 * No character, no attributes! Past end of line?
1893 * Do try matching with an empty line (could be the start of a region).
1894 */
1895 line = syn_getcurline();
1896 if (line[current_col] == NUL && current_col != 0)
1897 {
1898 /*
1899 * If we found a match after the last column, use it.
1900 */
1901 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1902 && next_match_col != MAXCOL)
1903 (void)push_next_match(NULL);
1904
1905 current_finished = TRUE;
1906 current_state_stored = FALSE;
1907 return 0;
1908 }
1909
1910 /* if the current or next character is NUL, we will finish the line now */
1911 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1912 {
1913 current_finished = TRUE;
1914 current_state_stored = FALSE;
1915 }
1916
1917 /*
1918 * When in the previous column there was a match but it could not be used
1919 * (empty match or already matched in this column) need to try again in
1920 * the next column.
1921 */
1922 if (try_next_column)
1923 {
1924 next_match_idx = -1;
1925 try_next_column = FALSE;
1926 }
1927
1928 /* Only check for keywords when not syncing and there are some. */
1929 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001930 && (syn_block->b_keywtab.ht_used > 0
1931 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001932
1933 /* Init the list of zero-width matches with a nextlist. This is used to
1934 * avoid matching the same item in the same position twice. */
1935 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1936
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001937 /* use syntax iskeyword option */
1938 save_chartab(buf_chartab);
1939
Bram Moolenaar071d4272004-06-13 20:20:40 +00001940 /*
1941 * Repeat matching keywords and patterns, to find contained items at the
1942 * same column. This stops when there are no extra matches at the current
1943 * column.
1944 */
1945 do
1946 {
1947 found_match = FALSE;
1948 keep_next_list = FALSE;
1949 syn_id = 0;
1950
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001951
Bram Moolenaar071d4272004-06-13 20:20:40 +00001952 /*
1953 * 1. Check for a current state.
1954 * Only when there is no current state, or if the current state may
1955 * contain other things, we need to check for keywords and patterns.
1956 * Always need to check for contained items if some item has the
1957 * "containedin" argument (takes extra time!).
1958 */
1959 if (current_state.ga_len)
1960 cur_si = &CUR_STATE(current_state.ga_len - 1);
1961 else
1962 cur_si = NULL;
1963
Bram Moolenaar860cae12010-06-05 23:22:07 +02001964 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001965 || cur_si->si_cont_list != NULL)
1966 {
1967 /*
1968 * 2. Check for keywords, if on a keyword char after a non-keyword
1969 * char. Don't do this when syncing.
1970 */
1971 if (do_keywords)
1972 {
1973 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001974 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001975 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001976 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00001977 - (has_mbyte
1978 ? (*mb_head_off)(line, line + current_col - 1)
Bram Moolenaar264b74f2019-01-24 17:18:42 +01001979 : 0) , syn_buf)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001980 {
1981 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001982 &endcol, &flags, &next_list, cur_si,
1983 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001984 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001985 {
1986 if (push_current_state(KEYWORD_IDX) == OK)
1987 {
1988 cur_si = &CUR_STATE(current_state.ga_len - 1);
1989 cur_si->si_m_startcol = current_col;
1990 cur_si->si_h_startpos.lnum = current_lnum;
1991 cur_si->si_h_startpos.col = 0; /* starts right away */
1992 cur_si->si_m_endpos.lnum = current_lnum;
1993 cur_si->si_m_endpos.col = endcol;
1994 cur_si->si_h_endpos.lnum = current_lnum;
1995 cur_si->si_h_endpos.col = endcol;
1996 cur_si->si_ends = TRUE;
1997 cur_si->si_end_idx = 0;
1998 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001999#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002000 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002001 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002002 if (current_state.ga_len > 1)
2003 cur_si->si_flags |=
2004 CUR_STATE(current_state.ga_len - 2).si_flags
2005 & HL_CONCEAL;
2006#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002007 cur_si->si_id = syn_id;
2008 cur_si->si_trans_id = syn_id;
2009 if (flags & HL_TRANSP)
2010 {
2011 if (current_state.ga_len < 2)
2012 {
2013 cur_si->si_attr = 0;
2014 cur_si->si_trans_id = 0;
2015 }
2016 else
2017 {
2018 cur_si->si_attr = CUR_STATE(
2019 current_state.ga_len - 2).si_attr;
2020 cur_si->si_trans_id = CUR_STATE(
2021 current_state.ga_len - 2).si_trans_id;
2022 }
2023 }
2024 else
2025 cur_si->si_attr = syn_id2attr(syn_id);
2026 cur_si->si_cont_list = NULL;
2027 cur_si->si_next_list = next_list;
2028 check_keepend();
2029 }
2030 else
2031 vim_free(next_list);
2032 }
2033 }
2034 }
2035
2036 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002037 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002038 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002039 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002040 {
2041 /*
2042 * If we didn't check for a match yet, or we are past it, check
2043 * for any match with a pattern.
2044 */
2045 if (next_match_idx < 0 || next_match_col < (int)current_col)
2046 {
2047 /*
2048 * Check all relevant patterns for a match at this
2049 * position. This is complicated, because matching with a
2050 * pattern takes quite a bit of time, thus we want to
2051 * avoid doing it when it's not needed.
2052 */
2053 next_match_idx = 0; /* no match in this line yet */
2054 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002055 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002056 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002057 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002058 if ( spp->sp_syncing == syncing
2059 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2060 && (spp->sp_type == SPTYPE_MATCH
2061 || spp->sp_type == SPTYPE_START)
2062 && (current_next_list != NULL
2063 ? in_id_list(NULL, current_next_list,
2064 &spp->sp_syn, 0)
2065 : (cur_si == NULL
2066 ? !(spp->sp_flags & HL_CONTAINED)
2067 : in_id_list(cur_si,
2068 cur_si->si_cont_list, &spp->sp_syn,
2069 spp->sp_flags & HL_CONTAINED))))
2070 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002071 int r;
2072
Bram Moolenaar071d4272004-06-13 20:20:40 +00002073 /* If we already tried matching in this line, and
2074 * there isn't a match before next_match_col, skip
2075 * this item. */
2076 if (spp->sp_line_id == current_line_id
2077 && spp->sp_startcol >= next_match_col)
2078 continue;
2079 spp->sp_line_id = current_line_id;
2080
2081 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2082 if (lc_col < 0)
2083 lc_col = 0;
2084
2085 regmatch.rmm_ic = spp->sp_ic;
2086 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002087 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002088 current_lnum,
2089 (colnr_T)lc_col,
Bram Moolenaard23a8232018-02-10 18:45:26 +01002090 IF_SYN_TIME(&spp->sp_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002091 spp->sp_prog = regmatch.regprog;
2092 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002093 {
2094 /* no match in this line, try another one */
2095 spp->sp_startcol = MAXCOL;
2096 continue;
2097 }
2098
2099 /*
2100 * Compute the first column of the match.
2101 */
2102 syn_add_start_off(&pos, &regmatch,
2103 spp, SPO_MS_OFF, -1);
2104 if (pos.lnum > current_lnum)
2105 {
2106 /* must have used end of match in a next line,
2107 * we can't handle that */
2108 spp->sp_startcol = MAXCOL;
2109 continue;
2110 }
2111 startcol = pos.col;
2112
2113 /* remember the next column where this pattern
2114 * matches in the current line */
2115 spp->sp_startcol = startcol;
2116
2117 /*
2118 * If a previously found match starts at a lower
2119 * column number, don't use this one.
2120 */
2121 if (startcol >= next_match_col)
2122 continue;
2123
2124 /*
2125 * If we matched this pattern at this position
2126 * before, skip it. Must retry in the next
2127 * column, because it may match from there.
2128 */
2129 if (did_match_already(idx, &zero_width_next_ga))
2130 {
2131 try_next_column = TRUE;
2132 continue;
2133 }
2134
2135 endpos.lnum = regmatch.endpos[0].lnum;
2136 endpos.col = regmatch.endpos[0].col;
2137
2138 /* Compute the highlight start. */
2139 syn_add_start_off(&hl_startpos, &regmatch,
2140 spp, SPO_HS_OFF, -1);
2141
2142 /* Compute the region start. */
2143 /* Default is to use the end of the match. */
2144 syn_add_end_off(&eos_pos, &regmatch,
2145 spp, SPO_RS_OFF, 0);
2146
2147 /*
2148 * Grab the external submatches before they get
2149 * overwritten. Reference count doesn't change.
2150 */
2151 unref_extmatch(cur_extmatch);
2152 cur_extmatch = re_extmatch_out;
2153 re_extmatch_out = NULL;
2154
2155 flags = 0;
2156 eoe_pos.lnum = 0; /* avoid warning */
2157 eoe_pos.col = 0;
2158 end_idx = 0;
2159 hl_endpos.lnum = 0;
2160
2161 /*
2162 * For a "oneline" the end must be found in the
2163 * same line too. Search for it after the end of
2164 * the match with the start pattern. Set the
2165 * resulting end positions at the same time.
2166 */
2167 if (spp->sp_type == SPTYPE_START
2168 && (spp->sp_flags & HL_ONELINE))
2169 {
2170 lpos_T startpos;
2171
2172 startpos = endpos;
2173 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2174 &flags, &eoe_pos, &end_idx, cur_extmatch);
2175 if (endpos.lnum == 0)
2176 continue; /* not found */
2177 }
2178
2179 /*
2180 * For a "match" the size must be > 0 after the
2181 * end offset needs has been added. Except when
2182 * syncing.
2183 */
2184 else if (spp->sp_type == SPTYPE_MATCH)
2185 {
2186 syn_add_end_off(&hl_endpos, &regmatch, spp,
2187 SPO_HE_OFF, 0);
2188 syn_add_end_off(&endpos, &regmatch, spp,
2189 SPO_ME_OFF, 0);
2190 if (endpos.lnum == current_lnum
2191 && (int)endpos.col + syncing < startcol)
2192 {
2193 /*
2194 * If an empty string is matched, may need
2195 * to try matching again at next column.
2196 */
2197 if (regmatch.startpos[0].col
2198 == regmatch.endpos[0].col)
2199 try_next_column = TRUE;
2200 continue;
2201 }
2202 }
2203
2204 /*
2205 * keep the best match so far in next_match_*
2206 */
2207 /* Highlighting must start after startpos and end
2208 * before endpos. */
2209 if (hl_startpos.lnum == current_lnum
2210 && (int)hl_startpos.col < startcol)
2211 hl_startpos.col = startcol;
2212 limit_pos_zero(&hl_endpos, &endpos);
2213
2214 next_match_idx = idx;
2215 next_match_col = startcol;
2216 next_match_m_endpos = endpos;
2217 next_match_h_endpos = hl_endpos;
2218 next_match_h_startpos = hl_startpos;
2219 next_match_flags = flags;
2220 next_match_eos_pos = eos_pos;
2221 next_match_eoe_pos = eoe_pos;
2222 next_match_end_idx = end_idx;
2223 unref_extmatch(next_match_extmatch);
2224 next_match_extmatch = cur_extmatch;
2225 cur_extmatch = NULL;
2226 }
2227 }
2228 }
2229
2230 /*
2231 * If we found a match at the current column, use it.
2232 */
2233 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2234 {
2235 synpat_T *lspp;
2236
2237 /* When a zero-width item matched which has a nextgroup,
2238 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002239 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002240 if (next_match_m_endpos.lnum == current_lnum
2241 && next_match_m_endpos.col == current_col
2242 && lspp->sp_next_list != NULL)
2243 {
2244 current_next_list = lspp->sp_next_list;
2245 current_next_flags = lspp->sp_flags;
2246 keep_next_list = TRUE;
2247 zero_width_next_list = TRUE;
2248
2249 /* Add the index to a list, so that we can check
2250 * later that we don't match it again (and cause an
2251 * endless loop). */
2252 if (ga_grow(&zero_width_next_ga, 1) == OK)
2253 {
2254 ((int *)(zero_width_next_ga.ga_data))
2255 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002256 }
2257 next_match_idx = -1;
2258 }
2259 else
2260 cur_si = push_next_match(cur_si);
2261 found_match = TRUE;
2262 }
2263 }
2264 }
2265
2266 /*
2267 * Handle searching for nextgroup match.
2268 */
2269 if (current_next_list != NULL && !keep_next_list)
2270 {
2271 /*
2272 * If a nextgroup was not found, continue looking for one if:
2273 * - this is an empty line and the "skipempty" option was given
2274 * - we are on white space and the "skipwhite" option was given
2275 */
2276 if (!found_match)
2277 {
2278 line = syn_getcurline();
2279 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002280 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002281 || ((current_next_flags & HL_SKIPEMPTY)
2282 && *line == NUL))
2283 break;
2284 }
2285
2286 /*
2287 * If a nextgroup was found: Use it, and continue looking for
2288 * contained matches.
2289 * If a nextgroup was not found: Continue looking for a normal
2290 * match.
2291 * When did set current_next_list for a zero-width item and no
2292 * match was found don't loop (would get stuck).
2293 */
2294 current_next_list = NULL;
2295 next_match_idx = -1;
2296 if (!zero_width_next_list)
2297 found_match = TRUE;
2298 }
2299
2300 } while (found_match);
2301
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002302 restore_chartab(buf_chartab);
2303
Bram Moolenaar071d4272004-06-13 20:20:40 +00002304 /*
2305 * Use attributes from the current state, if within its highlighting.
2306 * If not, use attributes from the current-but-one state, etc.
2307 */
2308 current_attr = 0;
2309#ifdef FEAT_EVAL
2310 current_id = 0;
2311 current_trans_id = 0;
2312#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002313#ifdef FEAT_CONCEAL
2314 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02002315 current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002316#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002317 if (cur_si != NULL)
2318 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002319#ifndef FEAT_EVAL
2320 int current_trans_id = 0;
2321#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002322 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2323 {
2324 sip = &CUR_STATE(idx);
2325 if ((current_lnum > sip->si_h_startpos.lnum
2326 || (current_lnum == sip->si_h_startpos.lnum
2327 && current_col >= sip->si_h_startpos.col))
2328 && (sip->si_h_endpos.lnum == 0
2329 || current_lnum < sip->si_h_endpos.lnum
2330 || (current_lnum == sip->si_h_endpos.lnum
2331 && current_col < sip->si_h_endpos.col)))
2332 {
2333 current_attr = sip->si_attr;
2334#ifdef FEAT_EVAL
2335 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002336#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002337 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002338#ifdef FEAT_CONCEAL
2339 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002340 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002341 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002342#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002343 break;
2344 }
2345 }
2346
Bram Moolenaar217ad922005-03-20 22:37:15 +00002347 if (can_spell != NULL)
2348 {
2349 struct sp_syn sps;
2350
2351 /*
2352 * set "can_spell" to TRUE if spell checking is supposed to be
2353 * done in the current item.
2354 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002355 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002356 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002357 /* There is no @Spell cluster: Do spelling for items without
2358 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002359 if (syn_block->b_nospell_cluster_id == 0
2360 || current_trans_id == 0)
2361 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002362 else
2363 {
2364 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002365 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002366 sps.cont_in_list = NULL;
2367 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2368 }
2369 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002370 else
2371 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002372 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002373 * the @Spell cluster. But not when @NoSpell is also there.
2374 * At the toplevel only spell check when ":syn spell toplevel"
2375 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002376 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002377 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002378 else
2379 {
2380 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002381 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002382 sps.cont_in_list = NULL;
2383 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2384
Bram Moolenaar860cae12010-06-05 23:22:07 +02002385 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002386 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002387 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002388 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2389 *can_spell = FALSE;
2390 }
2391 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002392 }
2393 }
2394
2395
Bram Moolenaar071d4272004-06-13 20:20:40 +00002396 /*
2397 * Check for end of current state (and the states before it) at the
2398 * next column. Don't do this for syncing, because we would miss a
2399 * single character match.
2400 * First check if the current state ends at the current column. It
2401 * may be for an empty match and a containing item might end in the
2402 * current column.
2403 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002404 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002405 {
2406 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002407 if (current_state.ga_len > 0
2408 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002409 {
2410 ++current_col;
2411 check_state_ends();
2412 --current_col;
2413 }
2414 }
2415 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002416 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002417 /* Default: Only do spelling when there is no @Spell cluster or when
2418 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002419 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2420 ? (syn_block->b_spell_cluster_id == 0)
2421 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002422
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002423 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002424 if (current_next_list != NULL
Bram Moolenaar069dafc2018-03-03 20:02:19 +01002425 && (line = syn_getcurline())[current_col] != NUL
2426 && line[current_col + 1] == NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002427 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2428 current_next_list = NULL;
2429
2430 if (zero_width_next_ga.ga_len > 0)
2431 ga_clear(&zero_width_next_ga);
2432
2433 /* No longer need external matches. But keep next_match_extmatch. */
2434 unref_extmatch(re_extmatch_out);
2435 re_extmatch_out = NULL;
2436 unref_extmatch(cur_extmatch);
2437
2438 return current_attr;
2439}
2440
2441
2442/*
2443 * Check if we already matched pattern "idx" at the current column.
2444 */
2445 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002446did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002447{
2448 int i;
2449
2450 for (i = current_state.ga_len; --i >= 0; )
2451 if (CUR_STATE(i).si_m_startcol == (int)current_col
2452 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2453 && CUR_STATE(i).si_idx == idx)
2454 return TRUE;
2455
2456 /* Zero-width matches with a nextgroup argument are not put on the syntax
2457 * stack, and can only be matched once anyway. */
2458 for (i = gap->ga_len; --i >= 0; )
2459 if (((int *)(gap->ga_data))[i] == idx)
2460 return TRUE;
2461
2462 return FALSE;
2463}
2464
2465/*
2466 * Push the next match onto the stack.
2467 */
2468 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002469push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002470{
2471 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002472#ifdef FEAT_CONCEAL
2473 int save_flags;
2474#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002475
Bram Moolenaar860cae12010-06-05 23:22:07 +02002476 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002477
2478 /*
2479 * Push the item in current_state stack;
2480 */
2481 if (push_current_state(next_match_idx) == OK)
2482 {
2483 /*
2484 * If it's a start-skip-end type that crosses lines, figure out how
2485 * much it continues in this line. Otherwise just fill in the length.
2486 */
2487 cur_si = &CUR_STATE(current_state.ga_len - 1);
2488 cur_si->si_h_startpos = next_match_h_startpos;
2489 cur_si->si_m_startcol = current_col;
2490 cur_si->si_m_lnum = current_lnum;
2491 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002492#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002493 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002494 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002495 if (current_state.ga_len > 1)
2496 cur_si->si_flags |=
2497 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2498#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002499 cur_si->si_next_list = spp->sp_next_list;
2500 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2501 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2502 {
2503 /* Try to find the end pattern in the current line */
2504 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2505 check_keepend();
2506 }
2507 else
2508 {
2509 cur_si->si_m_endpos = next_match_m_endpos;
2510 cur_si->si_h_endpos = next_match_h_endpos;
2511 cur_si->si_ends = TRUE;
2512 cur_si->si_flags |= next_match_flags;
2513 cur_si->si_eoe_pos = next_match_eoe_pos;
2514 cur_si->si_end_idx = next_match_end_idx;
2515 }
2516 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2517 keepend_level = current_state.ga_len - 1;
2518 check_keepend();
2519 update_si_attr(current_state.ga_len - 1);
2520
Bram Moolenaar860cae12010-06-05 23:22:07 +02002521#ifdef FEAT_CONCEAL
2522 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2523#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002524 /*
2525 * If the start pattern has another highlight group, push another item
2526 * on the stack for the start pattern.
2527 */
2528 if ( spp->sp_type == SPTYPE_START
2529 && spp->sp_syn_match_id != 0
2530 && push_current_state(next_match_idx) == OK)
2531 {
2532 cur_si = &CUR_STATE(current_state.ga_len - 1);
2533 cur_si->si_h_startpos = next_match_h_startpos;
2534 cur_si->si_m_startcol = current_col;
2535 cur_si->si_m_lnum = current_lnum;
2536 cur_si->si_m_endpos = next_match_eos_pos;
2537 cur_si->si_h_endpos = next_match_eos_pos;
2538 cur_si->si_ends = TRUE;
2539 cur_si->si_end_idx = 0;
2540 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002541#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002542 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002543 cur_si->si_flags |= save_flags;
2544 if (cur_si->si_flags & HL_CONCEALENDS)
2545 cur_si->si_flags |= HL_CONCEAL;
2546#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002547 cur_si->si_next_list = NULL;
2548 check_keepend();
2549 update_si_attr(current_state.ga_len - 1);
2550 }
2551 }
2552
2553 next_match_idx = -1; /* try other match next time */
2554
2555 return cur_si;
2556}
2557
2558/*
2559 * Check for end of current state (and the states before it).
2560 */
2561 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002562check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002563{
2564 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002565 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002566
2567 cur_si = &CUR_STATE(current_state.ga_len - 1);
2568 for (;;)
2569 {
2570 if (cur_si->si_ends
2571 && (cur_si->si_m_endpos.lnum < current_lnum
2572 || (cur_si->si_m_endpos.lnum == current_lnum
2573 && cur_si->si_m_endpos.col <= current_col)))
2574 {
2575 /*
2576 * If there is an end pattern group ID, highlight the end pattern
2577 * now. No need to pop the current item from the stack.
2578 * Only do this if the end pattern continues beyond the current
2579 * position.
2580 */
2581 if (cur_si->si_end_idx
2582 && (cur_si->si_eoe_pos.lnum > current_lnum
2583 || (cur_si->si_eoe_pos.lnum == current_lnum
2584 && cur_si->si_eoe_pos.col > current_col)))
2585 {
2586 cur_si->si_idx = cur_si->si_end_idx;
2587 cur_si->si_end_idx = 0;
2588 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2589 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2590 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002591#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002592 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002593 if (cur_si->si_flags & HL_CONCEALENDS)
2594 cur_si->si_flags |= HL_CONCEAL;
2595#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002596 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002597
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002598 /* nextgroup= should not match in the end pattern */
2599 current_next_list = NULL;
2600
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002601 /* what matches next may be different now, clear it */
2602 next_match_idx = 0;
2603 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002604 break;
2605 }
2606 else
2607 {
2608 /* handle next_list, unless at end of line and no "skipnl" or
2609 * "skipempty" */
2610 current_next_list = cur_si->si_next_list;
2611 current_next_flags = cur_si->si_flags;
2612 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2613 && syn_getcurline()[current_col] == NUL)
2614 current_next_list = NULL;
2615
2616 /* When the ended item has "extend", another item with
2617 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002618 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002619
2620 pop_current_state();
2621
2622 if (current_state.ga_len == 0)
2623 break;
2624
Bram Moolenaar81993f42008-01-11 20:27:45 +00002625 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002626 {
2627 syn_update_ends(FALSE);
2628 if (current_state.ga_len == 0)
2629 break;
2630 }
2631
2632 cur_si = &CUR_STATE(current_state.ga_len - 1);
2633
2634 /*
2635 * Only for a region the search for the end continues after
2636 * the end of the contained item. If the contained match
2637 * included the end-of-line, break here, the region continues.
2638 * Don't do this when:
2639 * - "keepend" is used for the contained item
2640 * - not at the end of the line (could be end="x$"me=e-1).
2641 * - "excludenl" is used (HL_HAS_EOL won't be set)
2642 */
2643 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002644 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002645 == SPTYPE_START
2646 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2647 {
2648 update_si_end(cur_si, (int)current_col, TRUE);
2649 check_keepend();
2650 if ((current_next_flags & HL_HAS_EOL)
2651 && keepend_level < 0
2652 && syn_getcurline()[current_col] == NUL)
2653 break;
2654 }
2655 }
2656 }
2657 else
2658 break;
2659 }
2660}
2661
2662/*
2663 * Update an entry in the current_state stack for a match or region. This
2664 * fills in si_attr, si_next_list and si_cont_list.
2665 */
2666 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002667update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002668{
2669 stateitem_T *sip = &CUR_STATE(idx);
2670 synpat_T *spp;
2671
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002672 /* This should not happen... */
2673 if (sip->si_idx < 0)
2674 return;
2675
Bram Moolenaar860cae12010-06-05 23:22:07 +02002676 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002677 if (sip->si_flags & HL_MATCH)
2678 sip->si_id = spp->sp_syn_match_id;
2679 else
2680 sip->si_id = spp->sp_syn.id;
2681 sip->si_attr = syn_id2attr(sip->si_id);
2682 sip->si_trans_id = sip->si_id;
2683 if (sip->si_flags & HL_MATCH)
2684 sip->si_cont_list = NULL;
2685 else
2686 sip->si_cont_list = spp->sp_cont_list;
2687
2688 /*
2689 * For transparent items, take attr from outer item.
2690 * Also take cont_list, if there is none.
2691 * Don't do this for the matchgroup of a start or end pattern.
2692 */
2693 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2694 {
2695 if (idx == 0)
2696 {
2697 sip->si_attr = 0;
2698 sip->si_trans_id = 0;
2699 if (sip->si_cont_list == NULL)
2700 sip->si_cont_list = ID_LIST_ALL;
2701 }
2702 else
2703 {
2704 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2705 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002706 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2707 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002708 if (sip->si_cont_list == NULL)
2709 {
2710 sip->si_flags |= HL_TRANS_CONT;
2711 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2712 }
2713 }
2714 }
2715}
2716
2717/*
2718 * Check the current stack for patterns with "keepend" flag.
2719 * Propagate the match-end to contained items, until a "skipend" item is found.
2720 */
2721 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002722check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002723{
2724 int i;
2725 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002726 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002727 stateitem_T *sip;
2728
2729 /*
2730 * This check can consume a lot of time; only do it from the level where
2731 * there really is a keepend.
2732 */
2733 if (keepend_level < 0)
2734 return;
2735
2736 /*
2737 * Find the last index of an "extend" item. "keepend" items before that
2738 * won't do anything. If there is no "extend" item "i" will be
2739 * "keepend_level" and all "keepend" items will work normally.
2740 */
2741 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2742 if (CUR_STATE(i).si_flags & HL_EXTEND)
2743 break;
2744
2745 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002746 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002747 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002748 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002749 for ( ; i < current_state.ga_len; ++i)
2750 {
2751 sip = &CUR_STATE(i);
2752 if (maxpos.lnum != 0)
2753 {
2754 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002755 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002756 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2757 sip->si_ends = TRUE;
2758 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002759 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2760 {
2761 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002762 || maxpos.lnum > sip->si_m_endpos.lnum
2763 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002764 && maxpos.col > sip->si_m_endpos.col))
2765 maxpos = sip->si_m_endpos;
2766 if (maxpos_h.lnum == 0
2767 || maxpos_h.lnum > sip->si_h_endpos.lnum
2768 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2769 && maxpos_h.col > sip->si_h_endpos.col))
2770 maxpos_h = sip->si_h_endpos;
2771 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002772 }
2773}
2774
2775/*
2776 * Update an entry in the current_state stack for a start-skip-end pattern.
2777 * This finds the end of the current item, if it's in the current line.
2778 *
2779 * Return the flags for the matched END.
2780 */
2781 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002782update_si_end(
2783 stateitem_T *sip,
2784 int startcol, /* where to start searching for the end */
2785 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002786{
2787 lpos_T startpos;
2788 lpos_T endpos;
2789 lpos_T hl_endpos;
2790 lpos_T end_endpos;
2791 int end_idx;
2792
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002793 /* return quickly for a keyword */
2794 if (sip->si_idx < 0)
2795 return;
2796
Bram Moolenaar071d4272004-06-13 20:20:40 +00002797 /* Don't update when it's already done. Can be a match of an end pattern
2798 * that started in a previous line. Watch out: can also be a "keepend"
2799 * from a containing item. */
2800 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2801 return;
2802
2803 /*
2804 * We need to find the end of the region. It may continue in the next
2805 * line.
2806 */
2807 end_idx = 0;
2808 startpos.lnum = current_lnum;
2809 startpos.col = startcol;
2810 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2811 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2812
2813 if (endpos.lnum == 0)
2814 {
2815 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002816 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002817 {
2818 /* a "oneline" never continues in the next line */
2819 sip->si_ends = TRUE;
2820 sip->si_m_endpos.lnum = current_lnum;
2821 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2822 }
2823 else
2824 {
2825 /* continues in the next line */
2826 sip->si_ends = FALSE;
2827 sip->si_m_endpos.lnum = 0;
2828 }
2829 sip->si_h_endpos = sip->si_m_endpos;
2830 }
2831 else
2832 {
2833 /* match within this line */
2834 sip->si_m_endpos = endpos;
2835 sip->si_h_endpos = hl_endpos;
2836 sip->si_eoe_pos = end_endpos;
2837 sip->si_ends = TRUE;
2838 sip->si_end_idx = end_idx;
2839 }
2840}
2841
2842/*
2843 * Add a new state to the current state stack.
2844 * It is cleared and the index set to "idx".
2845 * Return FAIL if it's not possible (out of memory).
2846 */
2847 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002848push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002849{
2850 if (ga_grow(&current_state, 1) == FAIL)
2851 return FAIL;
2852 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2853 CUR_STATE(current_state.ga_len).si_idx = idx;
2854 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002855 return OK;
2856}
2857
2858/*
2859 * Remove a state from the current_state stack.
2860 */
2861 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002862pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002863{
2864 if (current_state.ga_len)
2865 {
2866 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2867 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002868 }
2869 /* after the end of a pattern, try matching a keyword or pattern */
2870 next_match_idx = -1;
2871
2872 /* if first state with "keepend" is popped, reset keepend_level */
2873 if (keepend_level >= current_state.ga_len)
2874 keepend_level = -1;
2875}
2876
2877/*
2878 * Find the end of a start/skip/end syntax region after "startpos".
2879 * Only checks one line.
2880 * Also handles a match item that continued from a previous line.
2881 * If not found, the syntax item continues in the next line. m_endpos->lnum
2882 * will be 0.
2883 * If found, the end of the region and the end of the highlighting is
2884 * computed.
2885 */
2886 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002887find_endpos(
2888 int idx, /* index of the pattern */
2889 lpos_T *startpos, /* where to start looking for an END match */
2890 lpos_T *m_endpos, /* return: end of match */
2891 lpos_T *hl_endpos, /* return: end of highlighting */
2892 long *flagsp, /* return: flags of matching END */
2893 lpos_T *end_endpos, /* return: end of end pattern match */
2894 int *end_idx, /* return: group ID for end pat. match, or 0 */
2895 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002896{
2897 colnr_T matchcol;
2898 synpat_T *spp, *spp_skip;
2899 int start_idx;
2900 int best_idx;
2901 regmmatch_T regmatch;
2902 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2903 lpos_T pos;
2904 char_u *line;
2905 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002906 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002907
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002908 /* just in case we are invoked for a keyword */
2909 if (idx < 0)
2910 return;
2911
Bram Moolenaar071d4272004-06-13 20:20:40 +00002912 /*
2913 * Check for being called with a START pattern.
2914 * Can happen with a match that continues to the next line, because it
2915 * contained a region.
2916 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002917 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002918 if (spp->sp_type != SPTYPE_START)
2919 {
2920 *hl_endpos = *startpos;
2921 return;
2922 }
2923
2924 /*
2925 * Find the SKIP or first END pattern after the last START pattern.
2926 */
2927 for (;;)
2928 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002929 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002930 if (spp->sp_type != SPTYPE_START)
2931 break;
2932 ++idx;
2933 }
2934
2935 /*
2936 * Lookup the SKIP pattern (if present)
2937 */
2938 if (spp->sp_type == SPTYPE_SKIP)
2939 {
2940 spp_skip = spp;
2941 ++idx;
2942 }
2943 else
2944 spp_skip = NULL;
2945
2946 /* Setup external matches for syn_regexec(). */
2947 unref_extmatch(re_extmatch_in);
2948 re_extmatch_in = ref_extmatch(start_ext);
2949
2950 matchcol = startpos->col; /* start looking for a match at sstart */
2951 start_idx = idx; /* remember the first END pattern. */
2952 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002953
2954 /* use syntax iskeyword option */
2955 save_chartab(buf_chartab);
2956
Bram Moolenaar071d4272004-06-13 20:20:40 +00002957 for (;;)
2958 {
2959 /*
2960 * Find end pattern that matches first after "matchcol".
2961 */
2962 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002963 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002964 {
2965 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002966 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002967
Bram Moolenaar860cae12010-06-05 23:22:07 +02002968 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002969 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2970 break;
2971 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2972 if (lc_col < 0)
2973 lc_col = 0;
2974
2975 regmatch.rmm_ic = spp->sp_ic;
2976 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002977 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
2978 IF_SYN_TIME(&spp->sp_time));
2979 spp->sp_prog = regmatch.regprog;
2980 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002981 {
2982 if (best_idx == -1 || regmatch.startpos[0].col
2983 < best_regmatch.startpos[0].col)
2984 {
2985 best_idx = idx;
2986 best_regmatch.startpos[0] = regmatch.startpos[0];
2987 best_regmatch.endpos[0] = regmatch.endpos[0];
2988 }
2989 }
2990 }
2991
2992 /*
2993 * If all end patterns have been tried, and there is no match, the
2994 * item continues until end-of-line.
2995 */
2996 if (best_idx == -1)
2997 break;
2998
2999 /*
3000 * If the skip pattern matches before the end pattern,
3001 * continue searching after the skip pattern.
3002 */
3003 if (spp_skip != NULL)
3004 {
3005 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003006 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003007
3008 if (lc_col < 0)
3009 lc_col = 0;
3010 regmatch.rmm_ic = spp_skip->sp_ic;
3011 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003012 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3013 IF_SYN_TIME(&spp_skip->sp_time));
3014 spp_skip->sp_prog = regmatch.regprog;
3015 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003016 <= best_regmatch.startpos[0].col)
3017 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003018 int line_len;
3019
Bram Moolenaar071d4272004-06-13 20:20:40 +00003020 /* Add offset to skip pattern match */
3021 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3022
3023 /* If the skip pattern goes on to the next line, there is no
3024 * match with an end pattern in this line. */
3025 if (pos.lnum > startpos->lnum)
3026 break;
3027
3028 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003029 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003030
3031 /* take care of an empty match or negative offset */
3032 if (pos.col <= matchcol)
3033 ++matchcol;
3034 else if (pos.col <= regmatch.endpos[0].col)
3035 matchcol = pos.col;
3036 else
3037 /* Be careful not to jump over the NUL at the end-of-line */
3038 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003039 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003040 ++matchcol)
3041 ;
3042
3043 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003044 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003045 break;
3046
3047 continue; /* start with first end pattern again */
3048 }
3049 }
3050
3051 /*
3052 * Match from start pattern to end pattern.
3053 * Correct for match and highlight offset of end pattern.
3054 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003055 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003056 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3057 /* can't end before the start */
3058 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3059 m_endpos->col = startpos->col;
3060
3061 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3062 /* can't end before the start */
3063 if (end_endpos->lnum == startpos->lnum
3064 && end_endpos->col < startpos->col)
3065 end_endpos->col = startpos->col;
3066 /* can't end after the match */
3067 limit_pos(end_endpos, m_endpos);
3068
3069 /*
3070 * If the end group is highlighted differently, adjust the pointers.
3071 */
3072 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3073 {
3074 *end_idx = best_idx;
3075 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3076 {
3077 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3078 hl_endpos->col = best_regmatch.endpos[0].col;
3079 }
3080 else
3081 {
3082 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3083 hl_endpos->col = best_regmatch.startpos[0].col;
3084 }
3085 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3086
3087 /* can't end before the start */
3088 if (hl_endpos->lnum == startpos->lnum
3089 && hl_endpos->col < startpos->col)
3090 hl_endpos->col = startpos->col;
3091 limit_pos(hl_endpos, m_endpos);
3092
3093 /* now the match ends where the highlighting ends, it is turned
3094 * into the matchgroup for the end */
3095 *m_endpos = *hl_endpos;
3096 }
3097 else
3098 {
3099 *end_idx = 0;
3100 *hl_endpos = *end_endpos;
3101 }
3102
3103 *flagsp = spp->sp_flags;
3104
3105 had_match = TRUE;
3106 break;
3107 }
3108
3109 /* no match for an END pattern in this line */
3110 if (!had_match)
3111 m_endpos->lnum = 0;
3112
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003113 restore_chartab(buf_chartab);
3114
Bram Moolenaar071d4272004-06-13 20:20:40 +00003115 /* Remove external matches. */
3116 unref_extmatch(re_extmatch_in);
3117 re_extmatch_in = NULL;
3118}
3119
3120/*
3121 * Limit "pos" not to be after "limit".
3122 */
3123 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003124limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003125{
3126 if (pos->lnum > limit->lnum)
3127 *pos = *limit;
3128 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3129 pos->col = limit->col;
3130}
3131
3132/*
3133 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3134 */
3135 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003136limit_pos_zero(
3137 lpos_T *pos,
3138 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003139{
3140 if (pos->lnum == 0)
3141 *pos = *limit;
3142 else
3143 limit_pos(pos, limit);
3144}
3145
3146/*
3147 * Add offset to matched text for end of match or highlight.
3148 */
3149 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003150syn_add_end_off(
3151 lpos_T *result, /* returned position */
3152 regmmatch_T *regmatch, /* start/end of match */
3153 synpat_T *spp, /* matched pattern */
3154 int idx, /* index of offset */
3155 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003156{
3157 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003158 int off;
3159 char_u *base;
3160 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003161
3162 if (spp->sp_off_flags & (1 << idx))
3163 {
3164 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003165 col = regmatch->startpos[0].col;
3166 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003167 }
3168 else
3169 {
3170 result->lnum = regmatch->endpos[0].lnum;
3171 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003172 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003173 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003174 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3175 * is a matchgroup. Watch out for match with last NL in the buffer. */
3176 if (result->lnum > syn_buf->b_ml.ml_line_count)
3177 col = 0;
3178 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003179 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003180 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3181 p = base + col;
3182 if (off > 0)
3183 {
3184 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003185 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003186 }
3187 else if (off < 0)
3188 {
3189 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003190 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003191 }
3192 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003193 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003194 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003195}
3196
3197/*
3198 * Add offset to matched text for start of match or highlight.
3199 * Avoid resulting column to become negative.
3200 */
3201 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003202syn_add_start_off(
3203 lpos_T *result, /* returned position */
3204 regmmatch_T *regmatch, /* start/end of match */
3205 synpat_T *spp,
3206 int idx,
3207 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003208{
3209 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003210 int off;
3211 char_u *base;
3212 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003213
3214 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3215 {
3216 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003217 col = regmatch->endpos[0].col;
3218 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003219 }
3220 else
3221 {
3222 result->lnum = regmatch->startpos[0].lnum;
3223 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003224 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003225 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003226 if (result->lnum > syn_buf->b_ml.ml_line_count)
3227 {
3228 /* a "\n" at the end of the pattern may take us below the last line */
3229 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003230 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003231 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003232 if (off != 0)
3233 {
3234 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3235 p = base + col;
3236 if (off > 0)
3237 {
3238 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003239 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003240 }
3241 else if (off < 0)
3242 {
3243 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003244 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003245 }
3246 col = (int)(p - base);
3247 }
3248 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003249}
3250
3251/*
3252 * Get current line in syntax buffer.
3253 */
3254 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003255syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003256{
3257 return ml_get_buf(syn_buf, current_lnum, FALSE);
3258}
3259
3260/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003261 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003262 * Returns TRUE when there is a match.
3263 */
3264 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003265syn_regexec(
3266 regmmatch_T *rmp,
3267 linenr_T lnum,
3268 colnr_T col,
3269 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003270{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003271 int r;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003272#ifdef FEAT_RELTIME
3273 int timed_out = FALSE;
3274#endif
Bram Moolenaarf7512552013-06-06 14:55:19 +02003275#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003276 proftime_T pt;
3277
3278 if (syn_time_on)
3279 profile_start(&pt);
3280#endif
3281
Bram Moolenaarbcf94422018-06-23 14:21:42 +02003282 if (rmp->regprog == NULL)
3283 // This can happen if a previous call to vim_regexec_multi() tried to
3284 // use the NFA engine, which resulted in NFA_TOO_EXPENSIVE, and
3285 // compiling the pattern with the other engine fails.
3286 return FALSE;
3287
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003288 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003289 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
3290#ifdef FEAT_RELTIME
3291 syn_tm, &timed_out
3292#else
3293 NULL, NULL
3294#endif
3295 );
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003296
Bram Moolenaarf7512552013-06-06 14:55:19 +02003297#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003298 if (syn_time_on)
3299 {
3300 profile_end(&pt);
3301 profile_add(&st->total, &pt);
3302 if (profile_cmp(&pt, &st->slowest) < 0)
3303 st->slowest = pt;
3304 ++st->count;
3305 if (r > 0)
3306 ++st->match;
3307 }
3308#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003309#ifdef FEAT_RELTIME
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003310 if (timed_out && !syn_win->w_s->b_syn_slow)
3311 {
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003312 syn_win->w_s->b_syn_slow = TRUE;
Bram Moolenaar32526b32019-01-19 17:43:09 +01003313 msg(_("'redrawtime' exceeded, syntax highlighting disabled"));
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003314 }
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003315#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003316
3317 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003318 {
3319 rmp->startpos[0].lnum += lnum;
3320 rmp->endpos[0].lnum += lnum;
3321 return TRUE;
3322 }
3323 return FALSE;
3324}
3325
3326/*
3327 * Check one position in a line for a matching keyword.
3328 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003329 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003330 */
3331 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003332check_keyword_id(
3333 char_u *line,
3334 int startcol, /* position in line to check for keyword */
3335 int *endcolp, /* return: character after found keyword */
3336 long *flagsp, /* return: flags of matching keyword */
3337 short **next_listp, /* return: next_list of matching keyword */
3338 stateitem_T *cur_si, /* item at the top of the stack */
3339 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003340{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003341 keyentry_T *kp;
3342 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003343 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003344 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003345 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003346 hashtab_T *ht;
3347 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003348
3349 /* Find first character after the keyword. First character was already
3350 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003351 kwp = line + startcol;
3352 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003353 do
3354 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003355 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003356 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003357 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00003358 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003359 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003360 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003361
Bram Moolenaardad6b692005-01-25 22:14:34 +00003362 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003363 return 0;
3364
3365 /*
3366 * Must make a copy of the keyword, so we can add a NUL and make it
3367 * lowercase.
3368 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003369 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003370
3371 /*
3372 * Try twice:
3373 * 1. matching case
3374 * 2. ignoring case
3375 */
3376 for (round = 1; round <= 2; ++round)
3377 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003378 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003379 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003380 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003381 if (round == 2) /* ignore case */
3382 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003383
3384 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003385 * Find keywords that match. There can be several with different
3386 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003387 * When current_next_list is non-zero accept only that group, otherwise:
3388 * Accept a not-contained keyword at toplevel.
3389 * Accept a keyword at other levels only if it is in the contains list.
3390 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003391 hi = hash_find(ht, keyword);
3392 if (!HASHITEM_EMPTY(hi))
3393 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003394 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003395 if (current_next_list != 0
3396 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3397 : (cur_si == NULL
3398 ? !(kp->flags & HL_CONTAINED)
3399 : in_id_list(cur_si, cur_si->si_cont_list,
3400 &kp->k_syn, kp->flags & HL_CONTAINED)))
3401 {
3402 *endcolp = startcol + kwlen;
3403 *flagsp = kp->flags;
3404 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003405#ifdef FEAT_CONCEAL
3406 *ccharp = kp->k_char;
3407#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003408 return kp->k_syn.id;
3409 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003410 }
3411 }
3412 return 0;
3413}
3414
3415/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003416 * Handle ":syntax conceal" command.
3417 */
3418 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003419syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003420{
3421#ifdef FEAT_CONCEAL
3422 char_u *arg = eap->arg;
3423 char_u *next;
3424
3425 eap->nextcmd = find_nextcmd(arg);
3426 if (eap->skip)
3427 return;
3428
3429 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003430 if (*arg == NUL)
3431 {
3432 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003433 msg(_("syntax conceal on"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003434 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01003435 msg(_("syntax conceal off"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003436 }
3437 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003438 curwin->w_s->b_syn_conceal = TRUE;
3439 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3440 curwin->w_s->b_syn_conceal = FALSE;
3441 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003442 semsg(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003443#endif
3444}
3445
3446/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003447 * Handle ":syntax case" command.
3448 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003449 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003450syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003451{
3452 char_u *arg = eap->arg;
3453 char_u *next;
3454
3455 eap->nextcmd = find_nextcmd(arg);
3456 if (eap->skip)
3457 return;
3458
3459 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003460 if (*arg == NUL)
3461 {
3462 if (curwin->w_s->b_syn_ic)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003463 msg(_("syntax case ignore"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003464 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01003465 msg(_("syntax case match"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003466 }
3467 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003468 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003469 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003470 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003471 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003472 semsg(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003473}
3474
3475/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003476 * Handle ":syntax spell" command.
3477 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003478 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003479syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003480{
3481 char_u *arg = eap->arg;
3482 char_u *next;
3483
3484 eap->nextcmd = find_nextcmd(arg);
3485 if (eap->skip)
3486 return;
3487
3488 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003489 if (*arg == NUL)
3490 {
3491 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003492 msg(_("syntax spell toplevel"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003493 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003494 msg(_("syntax spell notoplevel"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003495 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01003496 msg(_("syntax spell default"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003497 }
3498 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003499 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003500 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003501 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003502 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003503 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003504 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003505 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003506 semsg(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003507 return;
3508 }
3509
3510 /* assume spell checking changed, force a redraw */
3511 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003512}
3513
3514/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003515 * Handle ":syntax iskeyword" command.
3516 */
3517 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003518syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003519{
3520 char_u *arg = eap->arg;
3521 char_u save_chartab[32];
3522 char_u *save_isk;
3523
3524 if (eap->skip)
3525 return;
3526
3527 arg = skipwhite(arg);
3528 if (*arg == NUL)
3529 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003530 msg_puts("\n");
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003531 if (curwin->w_s->b_syn_isk != empty_option)
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003532 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003533 msg_puts(_("syntax iskeyword "));
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003534 msg_outtrans(curwin->w_s->b_syn_isk);
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003535 }
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003536 else
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003537 msg_outtrans((char_u *)_("syntax iskeyword not set"));
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003538 }
3539 else
3540 {
3541 if (STRNICMP(arg, "clear", 5) == 0)
3542 {
3543 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3544 (size_t)32);
3545 clear_string_option(&curwin->w_s->b_syn_isk);
3546 }
3547 else
3548 {
3549 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3550 save_isk = curbuf->b_p_isk;
3551 curbuf->b_p_isk = vim_strsave(arg);
3552
3553 buf_init_chartab(curbuf, FALSE);
3554 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3555 (size_t)32);
3556 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3557 clear_string_option(&curwin->w_s->b_syn_isk);
3558 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3559 curbuf->b_p_isk = save_isk;
3560 }
3561 }
3562 redraw_win_later(curwin, NOT_VALID);
3563}
3564
3565/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003566 * Clear all syntax info for one buffer.
3567 */
3568 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003569syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003570{
3571 int i;
3572
Bram Moolenaar860cae12010-06-05 23:22:07 +02003573 block->b_syn_error = FALSE; /* clear previous error */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003574#ifdef FEAT_RELTIME
3575 block->b_syn_slow = FALSE; /* clear previous timeout */
3576#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003577 block->b_syn_ic = FALSE; /* Use case, by default */
3578 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3579 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003580#ifdef FEAT_CONCEAL
3581 block->b_syn_conceal = FALSE;
3582#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003583
3584 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003585 clear_keywtab(&block->b_keywtab);
3586 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003587
3588 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003589 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3590 syn_clear_pattern(block, i);
3591 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003592
3593 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003594 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3595 syn_clear_cluster(block, i);
3596 ga_clear(&block->b_syn_clusters);
3597 block->b_spell_cluster_id = 0;
3598 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003599
Bram Moolenaar860cae12010-06-05 23:22:07 +02003600 block->b_syn_sync_flags = 0;
3601 block->b_syn_sync_minlines = 0;
3602 block->b_syn_sync_maxlines = 0;
3603 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003604
Bram Moolenaar473de612013-06-08 18:19:48 +02003605 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003606 block->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003607 VIM_CLEAR(block->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003608#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003609 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003610#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003611 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003612
3613 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003614 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003615 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003616
3617 /* Reset the counter for ":syn include" */
3618 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003619}
3620
3621/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003622 * Get rid of ownsyntax for window "wp".
3623 */
3624 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003625reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003626{
3627 if (wp->w_s != &wp->w_buffer->b_s)
3628 {
3629 syntax_clear(wp->w_s);
3630 vim_free(wp->w_s);
3631 wp->w_s = &wp->w_buffer->b_s;
3632 }
3633}
3634
3635/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003636 * Clear syncing info for one buffer.
3637 */
3638 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003639syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003640{
3641 int i;
3642
3643 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003644 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3645 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3646 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003647
Bram Moolenaar860cae12010-06-05 23:22:07 +02003648 curwin->w_s->b_syn_sync_flags = 0;
3649 curwin->w_s->b_syn_sync_minlines = 0;
3650 curwin->w_s->b_syn_sync_maxlines = 0;
3651 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003652
Bram Moolenaar473de612013-06-08 18:19:48 +02003653 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003654 curwin->w_s->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003655 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003656 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003657
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003658 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003659}
3660
3661/*
3662 * Remove one pattern from the buffer's pattern list.
3663 */
3664 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003665syn_remove_pattern(
3666 synblock_T *block,
3667 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003668{
3669 synpat_T *spp;
3670
Bram Moolenaar860cae12010-06-05 23:22:07 +02003671 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003672#ifdef FEAT_FOLDING
3673 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003674 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003675#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003676 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003677 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003678 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3679 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003680}
3681
3682/*
3683 * Clear and free one syntax pattern. When clearing all, must be called from
3684 * last to first!
3685 */
3686 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003687syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003688{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003689 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003690 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003691 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003692 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003693 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003694 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3695 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3696 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003697 }
3698}
3699
3700/*
3701 * Clear and free one syntax cluster.
3702 */
3703 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003704syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003705{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003706 vim_free(SYN_CLSTR(block)[i].scl_name);
3707 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3708 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003709}
3710
3711/*
3712 * Handle ":syntax clear" command.
3713 */
3714 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003715syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003716{
3717 char_u *arg = eap->arg;
3718 char_u *arg_end;
3719 int id;
3720
3721 eap->nextcmd = find_nextcmd(arg);
3722 if (eap->skip)
3723 return;
3724
3725 /*
3726 * We have to disable this within ":syn include @group filename",
3727 * because otherwise @group would get deleted.
3728 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3729 * clear".
3730 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003731 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003732 return;
3733
3734 if (ends_excmd(*arg))
3735 {
3736 /*
3737 * No argument: Clear all syntax items.
3738 */
3739 if (syncing)
3740 syntax_sync_clear();
3741 else
3742 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003743 syntax_clear(curwin->w_s);
3744 if (curwin->w_s == &curwin->w_buffer->b_s)
3745 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003746 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003747 }
3748 }
3749 else
3750 {
3751 /*
3752 * Clear the group IDs that are in the argument.
3753 */
3754 while (!ends_excmd(*arg))
3755 {
3756 arg_end = skiptowhite(arg);
3757 if (*arg == '@')
3758 {
3759 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3760 if (id == 0)
3761 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003762 semsg(_("E391: No such syntax cluster: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003763 break;
3764 }
3765 else
3766 {
3767 /*
3768 * We can't physically delete a cluster without changing
3769 * the IDs of other clusters, so we do the next best thing
3770 * and make it empty.
3771 */
3772 short scl_id = id - SYNID_CLUSTER;
3773
Bram Moolenaard23a8232018-02-10 18:45:26 +01003774 VIM_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003775 }
3776 }
3777 else
3778 {
3779 id = syn_namen2id(arg, (int)(arg_end - arg));
3780 if (id == 0)
3781 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003782 semsg(_(e_nogroup), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003783 break;
3784 }
3785 else
3786 syn_clear_one(id, syncing);
3787 }
3788 arg = skipwhite(arg_end);
3789 }
3790 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003791 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003792 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003793}
3794
3795/*
3796 * Clear one syntax group for the current buffer.
3797 */
3798 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003799syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003800{
3801 synpat_T *spp;
3802 int idx;
3803
3804 /* Clear keywords only when not ":syn sync clear group-name" */
3805 if (!syncing)
3806 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003807 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3808 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003809 }
3810
3811 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003812 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003813 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003814 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003815 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3816 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003817 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003818 }
3819}
3820
3821/*
3822 * Handle ":syntax on" command.
3823 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003824 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003825syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003826{
3827 syn_cmd_onoff(eap, "syntax");
3828}
3829
3830/*
3831 * Handle ":syntax enable" command.
3832 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003833 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003834syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003835{
3836 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3837 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003838 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003839}
3840
3841/*
3842 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003843 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003844 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003845 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003846syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003847{
3848 eap->nextcmd = check_nextcmd(eap->arg);
3849 if (!eap->skip)
3850 {
3851 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3852 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003853 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003854 }
3855}
3856
3857/*
3858 * Handle ":syntax manual" command.
3859 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003860 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003861syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003862{
3863 syn_cmd_onoff(eap, "manual");
3864}
3865
3866/*
3867 * Handle ":syntax off" command.
3868 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003869 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003870syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003871{
3872 syn_cmd_onoff(eap, "nosyntax");
3873}
3874
3875 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003876syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003877{
3878 char_u buf[100];
3879
3880 eap->nextcmd = check_nextcmd(eap->arg);
3881 if (!eap->skip)
3882 {
3883 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003884 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003885 do_cmdline_cmd(buf);
3886 }
3887}
3888
3889/*
3890 * Handle ":syntax [list]" command: list current syntax words.
3891 */
3892 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003893syn_cmd_list(
3894 exarg_T *eap,
3895 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003896{
3897 char_u *arg = eap->arg;
3898 int id;
3899 char_u *arg_end;
3900
3901 eap->nextcmd = find_nextcmd(arg);
3902 if (eap->skip)
3903 return;
3904
Bram Moolenaar860cae12010-06-05 23:22:07 +02003905 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003906 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003907 msg(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003908 return;
3909 }
3910
3911 if (syncing)
3912 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003913 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003914 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003915 msg_puts(_("syncing on C-style comments"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003916 syn_lines_msg();
3917 syn_match_msg();
3918 return;
3919 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003920 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003921 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003922 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003923 msg_puts(_("no syncing"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003924 else
3925 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003926 msg_puts(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003927 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar32526b32019-01-19 17:43:09 +01003928 msg_puts(_(" lines before top line"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003929 syn_match_msg();
3930 }
3931 return;
3932 }
Bram Moolenaar32526b32019-01-19 17:43:09 +01003933 msg_puts_title(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003934 if (curwin->w_s->b_syn_sync_minlines > 0
3935 || curwin->w_s->b_syn_sync_maxlines > 0
3936 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003937 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003938 msg_puts(_("\nsyncing on items"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003939 syn_lines_msg();
3940 syn_match_msg();
3941 }
3942 }
3943 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01003944 msg_puts_title(_("\n--- Syntax items ---"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003945 if (ends_excmd(*arg))
3946 {
3947 /*
3948 * No argument: List all group IDs and all syntax clusters.
3949 */
3950 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3951 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003952 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003953 syn_list_cluster(id);
3954 }
3955 else
3956 {
3957 /*
3958 * List the group IDs and syntax clusters that are in the argument.
3959 */
3960 while (!ends_excmd(*arg) && !got_int)
3961 {
3962 arg_end = skiptowhite(arg);
3963 if (*arg == '@')
3964 {
3965 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3966 if (id == 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003967 semsg(_("E392: No such syntax cluster: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003968 else
3969 syn_list_cluster(id - SYNID_CLUSTER);
3970 }
3971 else
3972 {
3973 id = syn_namen2id(arg, (int)(arg_end - arg));
3974 if (id == 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003975 semsg(_(e_nogroup), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003976 else
3977 syn_list_one(id, syncing, TRUE);
3978 }
3979 arg = skipwhite(arg_end);
3980 }
3981 }
3982 eap->nextcmd = check_nextcmd(arg);
3983}
3984
3985 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003986syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003987{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003988 if (curwin->w_s->b_syn_sync_maxlines > 0
3989 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003990 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003991 msg_puts("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003992 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003993 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01003994 msg_puts(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003995 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3996 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003997 msg_puts(", ");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003998 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003999 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004000 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004001 msg_puts(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004002 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004003 }
Bram Moolenaar32526b32019-01-19 17:43:09 +01004004 msg_puts(_(" lines before top line"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004005 }
4006}
4007
4008 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004009syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004010{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004011 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004012 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004013 msg_puts(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004014 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004015 msg_puts(_(" line breaks"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004016 }
4017}
4018
4019static int last_matchgroup;
4020
4021struct name_list
4022{
4023 int flag;
4024 char *name;
4025};
4026
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004027static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004028
4029/*
4030 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4031 */
4032 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004033syn_list_one(
4034 int id,
4035 int syncing, /* when TRUE: list syncing items */
4036 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004037{
4038 int attr;
4039 int idx;
4040 int did_header = FALSE;
4041 synpat_T *spp;
4042 static struct name_list namelist1[] =
4043 {
4044 {HL_DISPLAY, "display"},
4045 {HL_CONTAINED, "contained"},
4046 {HL_ONELINE, "oneline"},
4047 {HL_KEEPEND, "keepend"},
4048 {HL_EXTEND, "extend"},
4049 {HL_EXCLUDENL, "excludenl"},
4050 {HL_TRANSP, "transparent"},
4051 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004052#ifdef FEAT_CONCEAL
4053 {HL_CONCEAL, "conceal"},
4054 {HL_CONCEALENDS, "concealends"},
4055#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004056 {0, NULL}
4057 };
4058 static struct name_list namelist2[] =
4059 {
4060 {HL_SKIPWHITE, "skipwhite"},
4061 {HL_SKIPNL, "skipnl"},
4062 {HL_SKIPEMPTY, "skipempty"},
4063 {0, NULL}
4064 };
4065
Bram Moolenaar8820b482017-03-16 17:23:31 +01004066 attr = HL_ATTR(HLF_D); /* highlight like directories */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004067
4068 /* list the keywords for "id" */
4069 if (!syncing)
4070 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004071 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4072 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004073 did_header, attr);
4074 }
4075
4076 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004077 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004078 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004079 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004080 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4081 continue;
4082
4083 (void)syn_list_header(did_header, 999, id);
4084 did_header = TRUE;
4085 last_matchgroup = 0;
4086 if (spp->sp_type == SPTYPE_MATCH)
4087 {
4088 put_pattern("match", ' ', spp, attr);
4089 msg_putchar(' ');
4090 }
4091 else if (spp->sp_type == SPTYPE_START)
4092 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004093 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4094 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4095 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4096 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4097 while (idx < curwin->w_s->b_syn_patterns.ga_len
4098 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4099 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004100 --idx;
4101 msg_putchar(' ');
4102 }
4103 syn_list_flags(namelist1, spp->sp_flags, attr);
4104
4105 if (spp->sp_cont_list != NULL)
4106 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4107
4108 if (spp->sp_syn.cont_in_list != NULL)
4109 put_id_list((char_u *)"containedin",
4110 spp->sp_syn.cont_in_list, attr);
4111
4112 if (spp->sp_next_list != NULL)
4113 {
4114 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4115 syn_list_flags(namelist2, spp->sp_flags, attr);
4116 }
4117 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4118 {
4119 if (spp->sp_flags & HL_SYNC_HERE)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004120 msg_puts_attr("grouphere", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004121 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004122 msg_puts_attr("groupthere", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004123 msg_putchar(' ');
4124 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004125 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004126 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4127 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004128 msg_puts("NONE");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004129 msg_putchar(' ');
4130 }
4131 }
4132
4133 /* list the link, if there is one */
4134 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4135 {
4136 (void)syn_list_header(did_header, 999, id);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004137 msg_puts_attr("links to", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004138 msg_putchar(' ');
4139 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4140 }
4141}
4142
4143 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004144syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004145{
4146 int i;
4147
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004148 for (i = 0; nlist[i].flag != 0; ++i)
4149 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004150 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004151 msg_puts_attr(nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004152 msg_putchar(' ');
4153 }
4154}
4155
4156/*
4157 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4158 */
4159 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004160syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004161{
4162 int endcol = 15;
4163
4164 /* slight hack: roughly duplicate the guts of syn_list_header() */
4165 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004166 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004167
4168 if (msg_col >= endcol) /* output at least one space */
4169 endcol = msg_col + 1;
4170 if (Columns <= endcol) /* avoid hang for tiny window */
4171 endcol = Columns - 1;
4172
4173 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004174 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004175 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004176 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004177 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004178 }
4179 else
4180 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004181 msg_puts_attr("cluster", HL_ATTR(HLF_D));
4182 msg_puts("=NONE");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004183 }
4184}
4185
4186 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004187put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004188{
4189 short *p;
4190
Bram Moolenaar32526b32019-01-19 17:43:09 +01004191 msg_puts_attr((char *)name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004192 msg_putchar('=');
4193 for (p = list; *p; ++p)
4194 {
4195 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4196 {
4197 if (p[1])
Bram Moolenaar32526b32019-01-19 17:43:09 +01004198 msg_puts("ALLBUT");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004199 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01004200 msg_puts("ALL");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004201 }
4202 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4203 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004204 msg_puts("TOP");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004205 }
4206 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4207 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004208 msg_puts("CONTAINED");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004209 }
4210 else if (*p >= SYNID_CLUSTER)
4211 {
4212 short scl_id = *p - SYNID_CLUSTER;
4213
4214 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004215 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004216 }
4217 else
4218 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4219 if (p[1])
4220 msg_putchar(',');
4221 }
4222 msg_putchar(' ');
4223}
4224
4225 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004226put_pattern(
4227 char *s,
4228 int c,
4229 synpat_T *spp,
4230 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004231{
4232 long n;
4233 int mask;
4234 int first;
4235 static char *sepchars = "/+=-#@\"|'^&";
4236 int i;
4237
4238 /* May have to write "matchgroup=group" */
4239 if (last_matchgroup != spp->sp_syn_match_id)
4240 {
4241 last_matchgroup = spp->sp_syn_match_id;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004242 msg_puts_attr("matchgroup", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004243 msg_putchar('=');
4244 if (last_matchgroup == 0)
4245 msg_outtrans((char_u *)"NONE");
4246 else
4247 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4248 msg_putchar(' ');
4249 }
4250
4251 /* output the name of the pattern and an '=' or ' ' */
Bram Moolenaar32526b32019-01-19 17:43:09 +01004252 msg_puts_attr(s, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004253 msg_putchar(c);
4254
4255 /* output the pattern, in between a char that is not in the pattern */
4256 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4257 if (sepchars[++i] == NUL)
4258 {
4259 i = 0; /* no good char found, just use the first one */
4260 break;
4261 }
4262 msg_putchar(sepchars[i]);
4263 msg_outtrans(spp->sp_pattern);
4264 msg_putchar(sepchars[i]);
4265
4266 /* output any pattern options */
4267 first = TRUE;
4268 for (i = 0; i < SPO_COUNT; ++i)
4269 {
4270 mask = (1 << i);
4271 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4272 {
4273 if (!first)
4274 msg_putchar(','); /* separate with commas */
Bram Moolenaar32526b32019-01-19 17:43:09 +01004275 msg_puts(spo_name_tab[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004276 n = spp->sp_offsets[i];
4277 if (i != SPO_LC_OFF)
4278 {
4279 if (spp->sp_off_flags & mask)
4280 msg_putchar('s');
4281 else
4282 msg_putchar('e');
4283 if (n > 0)
4284 msg_putchar('+');
4285 }
4286 if (n || i == SPO_LC_OFF)
4287 msg_outnum(n);
4288 first = FALSE;
4289 }
4290 }
4291 msg_putchar(' ');
4292}
4293
4294/*
4295 * List or clear the keywords for one syntax group.
4296 * Return TRUE if the header has been printed.
4297 */
4298 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004299syn_list_keywords(
4300 int id,
4301 hashtab_T *ht,
4302 int did_header, /* header has already been printed */
4303 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004304{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004305 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004306 hashitem_T *hi;
4307 keyentry_T *kp;
4308 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004309 int prev_contained = 0;
4310 short *prev_next_list = NULL;
4311 short *prev_cont_in_list = NULL;
4312 int prev_skipnl = 0;
4313 int prev_skipwhite = 0;
4314 int prev_skipempty = 0;
4315
Bram Moolenaar071d4272004-06-13 20:20:40 +00004316 /*
4317 * Unfortunately, this list of keywords is not sorted on alphabet but on
4318 * hash value...
4319 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004320 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004321 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004322 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004323 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004324 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004325 --todo;
4326 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004327 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004328 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004329 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004330 if (prev_contained != (kp->flags & HL_CONTAINED)
4331 || prev_skipnl != (kp->flags & HL_SKIPNL)
4332 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4333 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4334 || prev_cont_in_list != kp->k_syn.cont_in_list
4335 || prev_next_list != kp->next_list)
4336 outlen = 9999;
4337 else
4338 outlen = (int)STRLEN(kp->keyword);
4339 /* output "contained" and "nextgroup" on each line */
4340 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004341 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004342 prev_contained = 0;
4343 prev_next_list = NULL;
4344 prev_cont_in_list = NULL;
4345 prev_skipnl = 0;
4346 prev_skipwhite = 0;
4347 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004348 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004349 did_header = TRUE;
4350 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004351 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004352 msg_puts_attr("contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004354 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004355 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004356 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004357 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004358 put_id_list((char_u *)"containedin",
4359 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004360 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004361 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004362 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004363 if (kp->next_list != prev_next_list)
4364 {
4365 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4366 msg_putchar(' ');
4367 prev_next_list = kp->next_list;
4368 if (kp->flags & HL_SKIPNL)
4369 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004370 msg_puts_attr("skipnl", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004371 msg_putchar(' ');
4372 prev_skipnl = (kp->flags & HL_SKIPNL);
4373 }
4374 if (kp->flags & HL_SKIPWHITE)
4375 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004376 msg_puts_attr("skipwhite", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004377 msg_putchar(' ');
4378 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4379 }
4380 if (kp->flags & HL_SKIPEMPTY)
4381 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004382 msg_puts_attr("skipempty", attr);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004383 msg_putchar(' ');
4384 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4385 }
4386 }
4387 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004388 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004389 }
4390 }
4391 }
4392
4393 return did_header;
4394}
4395
4396 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004397syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004398{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004399 hashitem_T *hi;
4400 keyentry_T *kp;
4401 keyentry_T *kp_prev;
4402 keyentry_T *kp_next;
4403 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004404
Bram Moolenaardad6b692005-01-25 22:14:34 +00004405 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004406 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004407 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004408 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004409 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004410 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004411 --todo;
4412 kp_prev = NULL;
4413 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004414 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004415 if (kp->k_syn.id == id)
4416 {
4417 kp_next = kp->ke_next;
4418 if (kp_prev == NULL)
4419 {
4420 if (kp_next == NULL)
4421 hash_remove(ht, hi);
4422 else
4423 hi->hi_key = KE2HIKEY(kp_next);
4424 }
4425 else
4426 kp_prev->ke_next = kp_next;
4427 vim_free(kp->next_list);
4428 vim_free(kp->k_syn.cont_in_list);
4429 vim_free(kp);
4430 kp = kp_next;
4431 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004432 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004433 {
4434 kp_prev = kp;
4435 kp = kp->ke_next;
4436 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004437 }
4438 }
4439 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004440 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441}
4442
4443/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004444 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004445 */
4446 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004447clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004448{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004449 hashitem_T *hi;
4450 int todo;
4451 keyentry_T *kp;
4452 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004453
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004454 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004455 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004456 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004457 if (!HASHITEM_EMPTY(hi))
4458 {
4459 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004460 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004461 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004462 kp_next = kp->ke_next;
4463 vim_free(kp->next_list);
4464 vim_free(kp->k_syn.cont_in_list);
4465 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004466 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004467 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004468 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004469 hash_clear(ht);
4470 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004471}
4472
4473/*
4474 * Add a keyword to the list of keywords.
4475 */
4476 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004477add_keyword(
4478 char_u *name, /* name of keyword */
4479 int id, /* group ID for this keyword */
4480 int flags, /* flags for this keyword */
4481 short *cont_in_list, /* containedin for this keyword */
4482 short *next_list, /* nextgroup for this keyword */
4483 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004484{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004485 keyentry_T *kp;
4486 hashtab_T *ht;
4487 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004488 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004489 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004490 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004491
Bram Moolenaar860cae12010-06-05 23:22:07 +02004492 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004493 name_ic = str_foldcase(name, (int)STRLEN(name),
4494 name_folded, MAXKEYWLEN + 1);
4495 else
4496 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004497 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4498 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004499 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004500 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004501 kp->k_syn.id = id;
4502 kp->k_syn.inc_tag = current_syn_inc_tag;
4503 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004504 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004505 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004506 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004507 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004508 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004509
Bram Moolenaar860cae12010-06-05 23:22:07 +02004510 if (curwin->w_s->b_syn_ic)
4511 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004512 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004513 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004514
Bram Moolenaardad6b692005-01-25 22:14:34 +00004515 hash = hash_hash(kp->keyword);
4516 hi = hash_lookup(ht, kp->keyword, hash);
4517 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004518 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004519 /* new keyword, add to hashtable */
4520 kp->ke_next = NULL;
4521 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004522 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004523 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004524 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004525 /* keyword already exists, prepend to list */
4526 kp->ke_next = HI2KE(hi);
4527 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004528 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004529}
4530
4531/*
4532 * Get the start and end of the group name argument.
4533 * Return a pointer to the first argument.
4534 * Return NULL if the end of the command was found instead of further args.
4535 */
4536 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004537get_group_name(
4538 char_u *arg, /* start of the argument */
4539 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004540{
4541 char_u *rest;
4542
4543 *name_end = skiptowhite(arg);
4544 rest = skipwhite(*name_end);
4545
4546 /*
4547 * Check if there are enough arguments. The first argument may be a
4548 * pattern, where '|' is allowed, so only check for NUL.
4549 */
4550 if (ends_excmd(*arg) || *rest == NUL)
4551 return NULL;
4552 return rest;
4553}
4554
4555/*
4556 * Check for syntax command option arguments.
4557 * This can be called at any place in the list of arguments, and just picks
4558 * out the arguments that are known. Can be called several times in a row to
4559 * collect all options in between other arguments.
4560 * Return a pointer to the next argument (which isn't an option).
4561 * Return NULL for any error;
4562 */
4563 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004564get_syn_options(
4565 char_u *arg, /* next argument to be checked */
4566 syn_opt_arg_T *opt, /* various things */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004567 int *conceal_char UNUSED,
4568 int skip) /* TRUE if skipping over command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004569{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004570 char_u *gname_start, *gname;
4571 int syn_id;
4572 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004573 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004574 int i;
4575 int fidx;
4576 static struct flag
4577 {
4578 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004579 int argtype;
4580 int flags;
4581 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4582 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4583 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4584 {"eExXtTeEnNdD", 0, HL_EXTEND},
4585 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4586 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4587 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4588 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4589 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4590 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4591 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4592 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4593 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004594 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4595 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4596 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004597 {"cCoOnNtTaAiInNsS", 1, 0},
4598 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4599 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004600 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004601 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004602
4603 if (arg == NULL) /* already detected error */
4604 return NULL;
4605
Bram Moolenaar860cae12010-06-05 23:22:07 +02004606#ifdef FEAT_CONCEAL
4607 if (curwin->w_s->b_syn_conceal)
4608 opt->flags |= HL_CONCEAL;
4609#endif
4610
Bram Moolenaar071d4272004-06-13 20:20:40 +00004611 for (;;)
4612 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004613 /*
4614 * This is used very often when a large number of keywords is defined.
4615 * Need to skip quickly when no option name is found.
4616 * Also avoid tolower(), it's slow.
4617 */
4618 if (strchr(first_letters, *arg) == NULL)
4619 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004620
4621 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4622 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004623 p = flagtab[fidx].name;
4624 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4625 if (arg[len] != p[i] && arg[len] != p[i + 1])
4626 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004627 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004628 || (flagtab[fidx].argtype > 0
4629 ? arg[len] == '='
4630 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004631 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004632 if (opt->keyword
4633 && (flagtab[fidx].flags == HL_DISPLAY
4634 || flagtab[fidx].flags == HL_FOLD
4635 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004636 /* treat "display", "fold" and "extend" as a keyword */
4637 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004638 break;
4639 }
4640 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004641 if (fidx < 0) /* no match found */
4642 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004643
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004644 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004645 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004646 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004647 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004648 emsg(_("E395: contains argument not accepted here"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004649 return NULL;
4650 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004651 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004652 return NULL;
4653 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004654 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004655 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004656 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004657 return NULL;
4658 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004659 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004660 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004661 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004662 return NULL;
4663 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004664 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4665 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004666 /* cchar=? */
4667 if (has_mbyte)
4668 {
Bram Moolenaar264b74f2019-01-24 17:18:42 +01004669#ifdef FEAT_CONCEAL
Bram Moolenaar860cae12010-06-05 23:22:07 +02004670 *conceal_char = mb_ptr2char(arg + 6);
Bram Moolenaar264b74f2019-01-24 17:18:42 +01004671#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004672 arg += mb_ptr2len(arg + 6) - 1;
4673 }
4674 else
Bram Moolenaar56be9502010-06-06 14:20:26 +02004675 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004676#ifdef FEAT_CONCEAL
4677 *conceal_char = arg[6];
4678#else
4679 ;
4680#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004681 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004682#ifdef FEAT_CONCEAL
4683 if (!vim_isprintc_strict(*conceal_char))
4684 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004685 emsg(_("E844: invalid cchar value"));
Bram Moolenaar4124e722011-01-22 00:58:20 +01004686 return NULL;
4687 }
4688#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004689 arg = skipwhite(arg + 7);
4690 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004691 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004692 {
4693 opt->flags |= flagtab[fidx].flags;
4694 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004695
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004696 if (flagtab[fidx].flags == HL_SYNC_HERE
4697 || flagtab[fidx].flags == HL_SYNC_THERE)
4698 {
4699 if (opt->sync_idx == NULL)
4700 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004701 emsg(_("E393: group[t]here not accepted here"));
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004702 return NULL;
4703 }
4704 gname_start = arg;
4705 arg = skiptowhite(arg);
4706 if (gname_start == arg)
4707 return NULL;
4708 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4709 if (gname == NULL)
4710 return NULL;
4711 if (STRCMP(gname, "NONE") == 0)
4712 *opt->sync_idx = NONE_IDX;
4713 else
4714 {
4715 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004716 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4717 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4718 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004719 {
4720 *opt->sync_idx = i;
4721 break;
4722 }
4723 if (i < 0)
4724 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004725 semsg(_("E394: Didn't find region item for %s"), gname);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004726 vim_free(gname);
4727 return NULL;
4728 }
4729 }
4730
4731 vim_free(gname);
4732 arg = skipwhite(arg);
4733 }
4734#ifdef FEAT_FOLDING
4735 else if (flagtab[fidx].flags == HL_FOLD
4736 && foldmethodIsSyntax(curwin))
4737 /* Need to update folds later. */
4738 foldUpdateAll(curwin);
4739#endif
4740 }
4741 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004742
4743 return arg;
4744}
4745
4746/*
4747 * Adjustments to syntax item when declared in a ":syn include"'d file.
4748 * Set the contained flag, and if the item is not already contained, add it
4749 * to the specified top-level group, if any.
4750 */
4751 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004752syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004753{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004754 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004755 return;
4756 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004757 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004758 {
4759 /* We have to alloc this, because syn_combine_list() will free it. */
4760 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004761 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004762
4763 if (grp_list != NULL)
4764 {
4765 grp_list[0] = id;
4766 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004767 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004768 CLUSTER_ADD);
4769 }
4770 }
4771}
4772
4773/*
4774 * Handle ":syntax include [@{group-name}] filename" command.
4775 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004776 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004777syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004778{
4779 char_u *arg = eap->arg;
4780 int sgl_id = 1;
4781 char_u *group_name_end;
4782 char_u *rest;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004783 char *errormsg = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004784 int prev_toplvl_grp;
4785 int prev_syn_inc_tag;
4786 int source = FALSE;
4787
4788 eap->nextcmd = find_nextcmd(arg);
4789 if (eap->skip)
4790 return;
4791
4792 if (arg[0] == '@')
4793 {
4794 ++arg;
4795 rest = get_group_name(arg, &group_name_end);
4796 if (rest == NULL)
4797 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004798 emsg(_("E397: Filename required"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004799 return;
4800 }
4801 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004802 if (sgl_id == 0)
4803 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004804 /* separate_nextcmd() and expand_filename() depend on this */
4805 eap->arg = rest;
4806 }
4807
4808 /*
4809 * Everything that's left, up to the next command, should be the
4810 * filename to include.
4811 */
4812 eap->argt |= (XFILE | NOSPC);
4813 separate_nextcmd(eap);
4814 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4815 {
4816 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4817 * file. Need to expand the file name first. In other cases
4818 * ":runtime!" is used. */
4819 source = TRUE;
4820 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4821 {
4822 if (errormsg != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004823 emsg(errormsg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004824 return;
4825 }
4826 }
4827
4828 /*
4829 * Save and restore the existing top-level grouplist id and ":syn
4830 * include" tag around the actual inclusion.
4831 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004832 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4833 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004834 emsg(_("E847: Too many syntax includes"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004835 return;
4836 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004837 prev_syn_inc_tag = current_syn_inc_tag;
4838 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004839 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4840 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004841 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004842 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004843 semsg(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004844 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004845 current_syn_inc_tag = prev_syn_inc_tag;
4846}
4847
4848/*
4849 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4850 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004851 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004852syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004853{
4854 char_u *arg = eap->arg;
4855 char_u *group_name_end;
4856 int syn_id;
4857 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004858 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004859 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004860 char_u *kw;
4861 syn_opt_arg_T syn_opt_arg;
4862 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004863 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004864
4865 rest = get_group_name(arg, &group_name_end);
4866
4867 if (rest != NULL)
4868 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004869 if (eap->skip)
4870 syn_id = -1;
4871 else
4872 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004873 if (syn_id != 0)
4874 /* allocate a buffer, for removing backslashes in the keyword */
4875 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004876 if (keyword_copy != NULL)
4877 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004878 syn_opt_arg.flags = 0;
4879 syn_opt_arg.keyword = TRUE;
4880 syn_opt_arg.sync_idx = NULL;
4881 syn_opt_arg.has_cont_list = FALSE;
4882 syn_opt_arg.cont_in_list = NULL;
4883 syn_opt_arg.next_list = NULL;
4884
Bram Moolenaar071d4272004-06-13 20:20:40 +00004885 /*
4886 * The options given apply to ALL keywords, so all options must be
4887 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004888 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004889 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004890 cnt = 0;
4891 p = keyword_copy;
4892 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004893 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004894 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4895 eap->skip);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004896 if (rest == NULL || ends_excmd(*rest))
4897 break;
4898 /* Copy the keyword, removing backslashes, and add a NUL. */
Bram Moolenaar1c465442017-03-12 20:10:05 +01004899 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004900 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004901 if (*rest == '\\' && rest[1] != NUL)
4902 ++rest;
4903 *p++ = *rest++;
4904 }
4905 *p++ = NUL;
4906 ++cnt;
4907 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004908
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004909 if (!eap->skip)
4910 {
4911 /* Adjust flags for use of ":syn include". */
4912 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4913
4914 /*
4915 * 2: Add an entry for each keyword.
4916 */
4917 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4918 {
4919 for (p = vim_strchr(kw, '['); ; )
4920 {
4921 if (p != NULL)
4922 *p = NUL;
4923 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004924 syn_opt_arg.cont_in_list,
4925 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004926 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004927 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004928 if (p[1] == NUL)
4929 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004930 semsg(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004931 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004932 }
4933 if (p[1] == ']')
4934 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004935 if (p[2] != NUL)
4936 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004937 semsg(_("E890: trailing char after ']': %s]%s"),
Bram Moolenaar1560d072015-08-13 22:53:29 +02004938 kw, &p[2]);
4939 goto error;
4940 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004941 kw = p + 1; /* skip over the "]" */
4942 break;
4943 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004944 if (has_mbyte)
4945 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004946 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004947
4948 mch_memmove(p, p + 1, l);
4949 p += l;
4950 }
4951 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004952 {
4953 p[0] = p[1];
4954 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004955 }
4956 }
4957 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004958 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004959error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004960 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004961 vim_free(syn_opt_arg.cont_in_list);
4962 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004963 }
4964 }
4965
4966 if (rest != NULL)
4967 eap->nextcmd = check_nextcmd(rest);
4968 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004969 semsg(_(e_invarg2), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004970
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004971 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004972 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004973}
4974
4975/*
4976 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4977 *
4978 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4979 */
4980 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004981syn_cmd_match(
4982 exarg_T *eap,
4983 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004984{
4985 char_u *arg = eap->arg;
4986 char_u *group_name_end;
4987 char_u *rest;
4988 synpat_T item; /* the item found in the line */
4989 int syn_id;
4990 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004991 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004992 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004993 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004994
4995 /* Isolate the group name, check for validity */
4996 rest = get_group_name(arg, &group_name_end);
4997
4998 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004999 syn_opt_arg.flags = 0;
5000 syn_opt_arg.keyword = FALSE;
5001 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
5002 syn_opt_arg.has_cont_list = TRUE;
5003 syn_opt_arg.cont_list = NULL;
5004 syn_opt_arg.cont_in_list = NULL;
5005 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005006 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005007
5008 /* get the pattern. */
5009 init_syn_patterns();
5010 vim_memset(&item, 0, sizeof(item));
5011 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005012 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
5013 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005014
5015 /* Get options after the pattern */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005016 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005017
5018 if (rest != NULL) /* all arguments are valid */
5019 {
5020 /*
5021 * Check for trailing command and illegal trailing arguments.
5022 */
5023 eap->nextcmd = check_nextcmd(rest);
5024 if (!ends_excmd(*rest) || eap->skip)
5025 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005026 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005027 && (syn_id = syn_check_group(arg,
5028 (int)(group_name_end - arg))) != 0)
5029 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005030 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005031 /*
5032 * Store the pattern in the syn_items list
5033 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005034 idx = curwin->w_s->b_syn_patterns.ga_len;
5035 SYN_ITEMS(curwin->w_s)[idx] = item;
5036 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5037 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5038 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5039 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5040 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5041 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5042 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5043 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005044 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005045#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005046 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005047#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005048 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005049 curwin->w_s->b_syn_containedin = TRUE;
5050 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5051 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005052
5053 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005054 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005055 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005056#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005057 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005058 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005059#endif
5060
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005061 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005062 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005063 return; /* don't free the progs and patterns now */
5064 }
5065 }
5066
5067 /*
5068 * Something failed, free the allocated memory.
5069 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005070 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005071 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005072 vim_free(syn_opt_arg.cont_list);
5073 vim_free(syn_opt_arg.cont_in_list);
5074 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005075
5076 if (rest == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005077 semsg(_(e_invarg2), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005078}
5079
5080/*
5081 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5082 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5083 */
5084 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005085syn_cmd_region(
5086 exarg_T *eap,
5087 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005088{
5089 char_u *arg = eap->arg;
5090 char_u *group_name_end;
5091 char_u *rest; /* next arg, NULL on error */
5092 char_u *key_end;
5093 char_u *key = NULL;
5094 char_u *p;
5095 int item;
5096#define ITEM_START 0
5097#define ITEM_SKIP 1
5098#define ITEM_END 2
5099#define ITEM_MATCHGROUP 3
5100 struct pat_ptr
5101 {
5102 synpat_T *pp_synp; /* pointer to syn_pattern */
5103 int pp_matchgroup_id; /* matchgroup ID */
5104 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5105 } *(pat_ptrs[3]);
5106 /* patterns found in the line */
5107 struct pat_ptr *ppp;
5108 struct pat_ptr *ppp_next;
5109 int pat_count = 0; /* nr of syn_patterns found */
5110 int syn_id;
5111 int matchgroup_id = 0;
5112 int not_enough = FALSE; /* not enough arguments */
5113 int illegal = FALSE; /* illegal arguments */
5114 int success = FALSE;
5115 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005116 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005117 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005118
5119 /* Isolate the group name, check for validity */
5120 rest = get_group_name(arg, &group_name_end);
5121
5122 pat_ptrs[0] = NULL;
5123 pat_ptrs[1] = NULL;
5124 pat_ptrs[2] = NULL;
5125
5126 init_syn_patterns();
5127
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005128 syn_opt_arg.flags = 0;
5129 syn_opt_arg.keyword = FALSE;
5130 syn_opt_arg.sync_idx = NULL;
5131 syn_opt_arg.has_cont_list = TRUE;
5132 syn_opt_arg.cont_list = NULL;
5133 syn_opt_arg.cont_in_list = NULL;
5134 syn_opt_arg.next_list = NULL;
5135
Bram Moolenaar071d4272004-06-13 20:20:40 +00005136 /*
5137 * get the options, patterns and matchgroup.
5138 */
5139 while (rest != NULL && !ends_excmd(*rest))
5140 {
5141 /* Check for option arguments */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005142 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005143 if (rest == NULL || ends_excmd(*rest))
5144 break;
5145
5146 /* must be a pattern or matchgroup then */
5147 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005148 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005149 ++key_end;
5150 vim_free(key);
5151 key = vim_strnsave_up(rest, (int)(key_end - rest));
5152 if (key == NULL) /* out of memory */
5153 {
5154 rest = NULL;
5155 break;
5156 }
5157 if (STRCMP(key, "MATCHGROUP") == 0)
5158 item = ITEM_MATCHGROUP;
5159 else if (STRCMP(key, "START") == 0)
5160 item = ITEM_START;
5161 else if (STRCMP(key, "END") == 0)
5162 item = ITEM_END;
5163 else if (STRCMP(key, "SKIP") == 0)
5164 {
5165 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5166 {
5167 illegal = TRUE;
5168 break;
5169 }
5170 item = ITEM_SKIP;
5171 }
5172 else
5173 break;
5174 rest = skipwhite(key_end);
5175 if (*rest != '=')
5176 {
5177 rest = NULL;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005178 semsg(_("E398: Missing '=': %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005179 break;
5180 }
5181 rest = skipwhite(rest + 1);
5182 if (*rest == NUL)
5183 {
5184 not_enough = TRUE;
5185 break;
5186 }
5187
5188 if (item == ITEM_MATCHGROUP)
5189 {
5190 p = skiptowhite(rest);
5191 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5192 matchgroup_id = 0;
5193 else
5194 {
5195 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5196 if (matchgroup_id == 0)
5197 {
5198 illegal = TRUE;
5199 break;
5200 }
5201 }
5202 rest = skipwhite(p);
5203 }
5204 else
5205 {
5206 /*
5207 * Allocate room for a syn_pattern, and link it in the list of
5208 * syn_patterns for this item, at the start (because the list is
5209 * used from end to start).
5210 */
5211 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5212 if (ppp == NULL)
5213 {
5214 rest = NULL;
5215 break;
5216 }
5217 ppp->pp_next = pat_ptrs[item];
5218 pat_ptrs[item] = ppp;
5219 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5220 if (ppp->pp_synp == NULL)
5221 {
5222 rest = NULL;
5223 break;
5224 }
5225
5226 /*
5227 * Get the syntax pattern and the following offset(s).
5228 */
5229 /* Enable the appropriate \z specials. */
5230 if (item == ITEM_START)
5231 reg_do_extmatch = REX_SET;
5232 else if (item == ITEM_SKIP || item == ITEM_END)
5233 reg_do_extmatch = REX_USE;
5234 rest = get_syn_pattern(rest, ppp->pp_synp);
5235 reg_do_extmatch = 0;
5236 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005237 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005238 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5239 ppp->pp_matchgroup_id = matchgroup_id;
5240 ++pat_count;
5241 }
5242 }
5243 vim_free(key);
5244 if (illegal || not_enough)
5245 rest = NULL;
5246
5247 /*
5248 * Must have a "start" and "end" pattern.
5249 */
5250 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5251 pat_ptrs[ITEM_END] == NULL))
5252 {
5253 not_enough = TRUE;
5254 rest = NULL;
5255 }
5256
5257 if (rest != NULL)
5258 {
5259 /*
5260 * Check for trailing garbage or command.
5261 * If OK, add the item.
5262 */
5263 eap->nextcmd = check_nextcmd(rest);
5264 if (!ends_excmd(*rest) || eap->skip)
5265 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005266 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005267 && (syn_id = syn_check_group(arg,
5268 (int)(group_name_end - arg))) != 0)
5269 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005270 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005271 /*
5272 * Store the start/skip/end in the syn_items list
5273 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005274 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005275 for (item = ITEM_START; item <= ITEM_END; ++item)
5276 {
5277 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5278 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005279 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5280 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5281 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005282 (item == ITEM_START) ? SPTYPE_START :
5283 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005284 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5285 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005286 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5287 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005288 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005289 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005290#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005291 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005292#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005293 if (item == ITEM_START)
5294 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005295 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005296 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005297 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005298 syn_opt_arg.cont_in_list;
5299 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005300 curwin->w_s->b_syn_containedin = TRUE;
5301 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005302 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005303 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005304 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005305 ++idx;
5306#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005307 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005308 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005309#endif
5310 }
5311 }
5312
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005313 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005314 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005315 success = TRUE; /* don't free the progs and patterns now */
5316 }
5317 }
5318
5319 /*
5320 * Free the allocated memory.
5321 */
5322 for (item = ITEM_START; item <= ITEM_END; ++item)
5323 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5324 {
5325 if (!success)
5326 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005327 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005328 vim_free(ppp->pp_synp->sp_pattern);
5329 }
5330 vim_free(ppp->pp_synp);
5331 ppp_next = ppp->pp_next;
5332 vim_free(ppp);
5333 }
5334
5335 if (!success)
5336 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005337 vim_free(syn_opt_arg.cont_list);
5338 vim_free(syn_opt_arg.cont_in_list);
5339 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005340 if (not_enough)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005341 semsg(_("E399: Not enough arguments: syntax region %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005342 else if (illegal || rest == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005343 semsg(_(e_invarg2), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005344 }
5345}
5346
5347/*
5348 * A simple syntax group ID comparison function suitable for use in qsort()
5349 */
5350 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005351syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005352{
5353 const short *s1 = v1;
5354 const short *s2 = v2;
5355
5356 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5357}
5358
5359/*
5360 * Combines lists of syntax clusters.
5361 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5362 */
5363 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005364syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005365{
5366 int count1 = 0;
5367 int count2 = 0;
5368 short *g1;
5369 short *g2;
5370 short *clstr = NULL;
5371 int count;
5372 int round;
5373
5374 /*
5375 * Handle degenerate cases.
5376 */
5377 if (*clstr2 == NULL)
5378 return;
5379 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5380 {
5381 if (list_op == CLUSTER_REPLACE)
5382 vim_free(*clstr1);
5383 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5384 *clstr1 = *clstr2;
5385 else
5386 vim_free(*clstr2);
5387 return;
5388 }
5389
5390 for (g1 = *clstr1; *g1; g1++)
5391 ++count1;
5392 for (g2 = *clstr2; *g2; g2++)
5393 ++count2;
5394
5395 /*
5396 * For speed purposes, sort both lists.
5397 */
5398 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5399 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5400
5401 /*
5402 * We proceed in two passes; in round 1, we count the elements to place
5403 * in the new list, and in round 2, we allocate and populate the new
5404 * list. For speed, we use a mergesort-like method, adding the smaller
5405 * of the current elements in each list to the new list.
5406 */
5407 for (round = 1; round <= 2; round++)
5408 {
5409 g1 = *clstr1;
5410 g2 = *clstr2;
5411 count = 0;
5412
5413 /*
5414 * First, loop through the lists until one of them is empty.
5415 */
5416 while (*g1 && *g2)
5417 {
5418 /*
5419 * We always want to add from the first list.
5420 */
5421 if (*g1 < *g2)
5422 {
5423 if (round == 2)
5424 clstr[count] = *g1;
5425 count++;
5426 g1++;
5427 continue;
5428 }
5429 /*
5430 * We only want to add from the second list if we're adding the
5431 * lists.
5432 */
5433 if (list_op == CLUSTER_ADD)
5434 {
5435 if (round == 2)
5436 clstr[count] = *g2;
5437 count++;
5438 }
5439 if (*g1 == *g2)
5440 g1++;
5441 g2++;
5442 }
5443
5444 /*
5445 * Now add the leftovers from whichever list didn't get finished
5446 * first. As before, we only want to add from the second list if
5447 * we're adding the lists.
5448 */
5449 for (; *g1; g1++, count++)
5450 if (round == 2)
5451 clstr[count] = *g1;
5452 if (list_op == CLUSTER_ADD)
5453 for (; *g2; g2++, count++)
5454 if (round == 2)
5455 clstr[count] = *g2;
5456
5457 if (round == 1)
5458 {
5459 /*
5460 * If the group ended up empty, we don't need to allocate any
5461 * space for it.
5462 */
5463 if (count == 0)
5464 {
5465 clstr = NULL;
5466 break;
5467 }
5468 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5469 if (clstr == NULL)
5470 break;
5471 clstr[count] = 0;
5472 }
5473 }
5474
5475 /*
5476 * Finally, put the new list in place.
5477 */
5478 vim_free(*clstr1);
5479 vim_free(*clstr2);
5480 *clstr1 = clstr;
5481}
5482
5483/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005484 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005485 * If it is not found, 0 is returned.
5486 */
5487 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005488syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005489{
5490 int i;
5491 char_u *name_u;
5492
5493 /* Avoid using stricmp() too much, it's slow on some systems */
5494 name_u = vim_strsave_up(name);
5495 if (name_u == NULL)
5496 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005497 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5498 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5499 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005500 break;
5501 vim_free(name_u);
5502 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5503}
5504
5505/*
5506 * Like syn_scl_name2id(), but take a pointer + length argument.
5507 */
5508 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005509syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005510{
5511 char_u *name;
5512 int id = 0;
5513
5514 name = vim_strnsave(linep, len);
5515 if (name != NULL)
5516 {
5517 id = syn_scl_name2id(name);
5518 vim_free(name);
5519 }
5520 return id;
5521}
5522
5523/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005524 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005525 * The argument is a pointer to the name and the length of the name.
5526 * If it doesn't exist yet, a new entry is created.
5527 * Return 0 for failure.
5528 */
5529 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005530syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005531{
5532 int id;
5533 char_u *name;
5534
5535 name = vim_strnsave(pp, len);
5536 if (name == NULL)
5537 return 0;
5538
5539 id = syn_scl_name2id(name);
5540 if (id == 0) /* doesn't exist yet */
5541 id = syn_add_cluster(name);
5542 else
5543 vim_free(name);
5544 return id;
5545}
5546
5547/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005548 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005549 * "name" must be an allocated string, it will be consumed.
5550 * Return 0 for failure.
5551 */
5552 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005553syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005554{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005555 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005556
5557 /*
5558 * First call for this growarray: init growing array.
5559 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005560 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005561 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005562 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5563 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005564 }
5565
Bram Moolenaar42431a72011-04-01 14:44:59 +02005566 len = curwin->w_s->b_syn_clusters.ga_len;
5567 if (len >= MAX_CLUSTER_ID)
5568 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005569 emsg(_("E848: Too many syntax clusters"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02005570 vim_free(name);
5571 return 0;
5572 }
5573
Bram Moolenaar071d4272004-06-13 20:20:40 +00005574 /*
5575 * Make room for at least one other cluster entry.
5576 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005577 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005578 {
5579 vim_free(name);
5580 return 0;
5581 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005582
Bram Moolenaar860cae12010-06-05 23:22:07 +02005583 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5584 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5585 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5586 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5587 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005588
Bram Moolenaar217ad922005-03-20 22:37:15 +00005589 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005590 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005591 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005592 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005593
Bram Moolenaar071d4272004-06-13 20:20:40 +00005594 return len + SYNID_CLUSTER;
5595}
5596
5597/*
5598 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5599 * [add={groupname},..] [remove={groupname},..]".
5600 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005601 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005602syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005603{
5604 char_u *arg = eap->arg;
5605 char_u *group_name_end;
5606 char_u *rest;
5607 int scl_id;
5608 short *clstr_list;
5609 int got_clstr = FALSE;
5610 int opt_len;
5611 int list_op;
5612
5613 eap->nextcmd = find_nextcmd(arg);
5614 if (eap->skip)
5615 return;
5616
5617 rest = get_group_name(arg, &group_name_end);
5618
5619 if (rest != NULL)
5620 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005621 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5622 if (scl_id == 0)
5623 return;
5624 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005625
5626 for (;;)
5627 {
5628 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005629 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005630 {
5631 opt_len = 3;
5632 list_op = CLUSTER_ADD;
5633 }
5634 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005635 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005636 {
5637 opt_len = 6;
5638 list_op = CLUSTER_SUBTRACT;
5639 }
5640 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005641 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005642 {
5643 opt_len = 8;
5644 list_op = CLUSTER_REPLACE;
5645 }
5646 else
5647 break;
5648
5649 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005650 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005651 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005652 semsg(_(e_invarg2), rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005653 break;
5654 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005655 if (scl_id >= 0)
5656 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005657 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005658 else
5659 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005660 got_clstr = TRUE;
5661 }
5662
5663 if (got_clstr)
5664 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005665 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005666 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005667 }
5668 }
5669
5670 if (!got_clstr)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005671 emsg(_("E400: No cluster specified"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005672 if (rest == NULL || !ends_excmd(*rest))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005673 semsg(_(e_invarg2), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005674}
5675
5676/*
5677 * On first call for current buffer: Init growing array.
5678 */
5679 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005680init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005681{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005682 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5683 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005684}
5685
5686/*
5687 * Get one pattern for a ":syntax match" or ":syntax region" command.
5688 * Stores the pattern and program in a synpat_T.
5689 * Returns a pointer to the next argument, or NULL in case of an error.
5690 */
5691 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005692get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005693{
5694 char_u *end;
5695 int *p;
5696 int idx;
5697 char_u *cpo_save;
5698
5699 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005700 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005701 return NULL;
5702
5703 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5704 if (*end != *arg) /* end delimiter not found */
5705 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005706 semsg(_("E401: Pattern delimiter not found: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005707 return NULL;
5708 }
5709 /* store the pattern and compiled regexp program */
5710 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5711 return NULL;
5712
5713 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5714 cpo_save = p_cpo;
5715 p_cpo = (char_u *)"";
5716 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5717 p_cpo = cpo_save;
5718
5719 if (ci->sp_prog == NULL)
5720 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005721 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005722#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005723 syn_clear_time(&ci->sp_time);
5724#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005725
5726 /*
5727 * Check for a match, highlight or region offset.
5728 */
5729 ++end;
5730 do
5731 {
5732 for (idx = SPO_COUNT; --idx >= 0; )
5733 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5734 break;
5735 if (idx >= 0)
5736 {
5737 p = &(ci->sp_offsets[idx]);
5738 if (idx != SPO_LC_OFF)
5739 switch (end[3])
5740 {
5741 case 's': break;
5742 case 'b': break;
5743 case 'e': idx += SPO_COUNT; break;
5744 default: idx = -1; break;
5745 }
5746 if (idx >= 0)
5747 {
5748 ci->sp_off_flags |= (1 << idx);
5749 if (idx == SPO_LC_OFF) /* lc=99 */
5750 {
5751 end += 3;
5752 *p = getdigits(&end);
5753
5754 /* "lc=" offset automatically sets "ms=" offset */
5755 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5756 {
5757 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5758 ci->sp_offsets[SPO_MS_OFF] = *p;
5759 }
5760 }
5761 else /* yy=x+99 */
5762 {
5763 end += 4;
5764 if (*end == '+')
5765 {
5766 ++end;
5767 *p = getdigits(&end); /* positive offset */
5768 }
5769 else if (*end == '-')
5770 {
5771 ++end;
5772 *p = -getdigits(&end); /* negative offset */
5773 }
5774 }
5775 if (*end != ',')
5776 break;
5777 ++end;
5778 }
5779 }
5780 } while (idx >= 0);
5781
Bram Moolenaar1c465442017-03-12 20:10:05 +01005782 if (!ends_excmd(*end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005783 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005784 semsg(_("E402: Garbage after pattern: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005785 return NULL;
5786 }
5787 return skipwhite(end);
5788}
5789
5790/*
5791 * Handle ":syntax sync .." command.
5792 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005793 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005794syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005795{
5796 char_u *arg_start = eap->arg;
5797 char_u *arg_end;
5798 char_u *key = NULL;
5799 char_u *next_arg;
5800 int illegal = FALSE;
5801 int finished = FALSE;
5802 long n;
5803 char_u *cpo_save;
5804
5805 if (ends_excmd(*arg_start))
5806 {
5807 syn_cmd_list(eap, TRUE);
5808 return;
5809 }
5810
5811 while (!ends_excmd(*arg_start))
5812 {
5813 arg_end = skiptowhite(arg_start);
5814 next_arg = skipwhite(arg_end);
5815 vim_free(key);
5816 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5817 if (STRCMP(key, "CCOMMENT") == 0)
5818 {
5819 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005820 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005821 if (!ends_excmd(*next_arg))
5822 {
5823 arg_end = skiptowhite(next_arg);
5824 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005825 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005826 (int)(arg_end - next_arg));
5827 next_arg = skipwhite(arg_end);
5828 }
5829 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005830 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005831 }
5832 else if ( STRNCMP(key, "LINES", 5) == 0
5833 || STRNCMP(key, "MINLINES", 8) == 0
5834 || STRNCMP(key, "MAXLINES", 8) == 0
5835 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5836 {
5837 if (key[4] == 'S')
5838 arg_end = key + 6;
5839 else if (key[0] == 'L')
5840 arg_end = key + 11;
5841 else
5842 arg_end = key + 9;
5843 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5844 {
5845 illegal = TRUE;
5846 break;
5847 }
5848 n = getdigits(&arg_end);
5849 if (!eap->skip)
5850 {
5851 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005852 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005853 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005854 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005855 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005856 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005857 }
5858 }
5859 else if (STRCMP(key, "FROMSTART") == 0)
5860 {
5861 if (!eap->skip)
5862 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005863 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5864 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005865 }
5866 }
5867 else if (STRCMP(key, "LINECONT") == 0)
5868 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005869 if (*next_arg == NUL) /* missing pattern */
5870 {
5871 illegal = TRUE;
5872 break;
5873 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005874 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005875 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005876 emsg(_("E403: syntax sync: line continuations pattern specified twice"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005877 finished = TRUE;
5878 break;
5879 }
5880 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5881 if (*arg_end != *next_arg) /* end delimiter not found */
5882 {
5883 illegal = TRUE;
5884 break;
5885 }
5886
5887 if (!eap->skip)
5888 {
5889 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005890 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005891 (int)(arg_end - next_arg - 1))) == NULL)
5892 {
5893 finished = TRUE;
5894 break;
5895 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005896 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005897
5898 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5899 cpo_save = p_cpo;
5900 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005901 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005902 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005903 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005904#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005905 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5906#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005907
Bram Moolenaar860cae12010-06-05 23:22:07 +02005908 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005909 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01005910 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005911 finished = TRUE;
5912 break;
5913 }
5914 }
5915 next_arg = skipwhite(arg_end + 1);
5916 }
5917 else
5918 {
5919 eap->arg = next_arg;
5920 if (STRCMP(key, "MATCH") == 0)
5921 syn_cmd_match(eap, TRUE);
5922 else if (STRCMP(key, "REGION") == 0)
5923 syn_cmd_region(eap, TRUE);
5924 else if (STRCMP(key, "CLEAR") == 0)
5925 syn_cmd_clear(eap, TRUE);
5926 else
5927 illegal = TRUE;
5928 finished = TRUE;
5929 break;
5930 }
5931 arg_start = next_arg;
5932 }
5933 vim_free(key);
5934 if (illegal)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005935 semsg(_("E404: Illegal arguments: %s"), arg_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005936 else if (!finished)
5937 {
5938 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005939 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005940 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005941 }
5942}
5943
5944/*
5945 * Convert a line of highlight group names into a list of group ID numbers.
5946 * "arg" should point to the "contains" or "nextgroup" keyword.
5947 * "arg" is advanced to after the last group name.
5948 * Careful: the argument is modified (NULs added).
5949 * returns FAIL for some error, OK for success.
5950 */
5951 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005952get_id_list(
5953 char_u **arg,
5954 int keylen, /* length of keyword */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005955 short **list, /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00005956 NULL, the list is silently skipped! */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005957 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005958{
5959 char_u *p = NULL;
5960 char_u *end;
5961 int round;
5962 int count;
5963 int total_count = 0;
5964 short *retval = NULL;
5965 char_u *name;
5966 regmatch_T regmatch;
5967 int id;
5968 int i;
5969 int failed = FALSE;
5970
5971 /*
5972 * We parse the list twice:
5973 * round == 1: count the number of items, allocate the array.
5974 * round == 2: fill the array with the items.
5975 * In round 1 new groups may be added, causing the number of items to
5976 * grow when a regexp is used. In that case round 1 is done once again.
5977 */
5978 for (round = 1; round <= 2; ++round)
5979 {
5980 /*
5981 * skip "contains"
5982 */
5983 p = skipwhite(*arg + keylen);
5984 if (*p != '=')
5985 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005986 semsg(_("E405: Missing equal sign: %s"), *arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005987 break;
5988 }
5989 p = skipwhite(p + 1);
5990 if (ends_excmd(*p))
5991 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005992 semsg(_("E406: Empty argument: %s"), *arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005993 break;
5994 }
5995
5996 /*
5997 * parse the arguments after "contains"
5998 */
5999 count = 0;
6000 while (!ends_excmd(*p))
6001 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01006002 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006003 ;
6004 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
6005 if (name == NULL)
6006 {
6007 failed = TRUE;
6008 break;
6009 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006010 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006011 if ( STRCMP(name + 1, "ALLBUT") == 0
6012 || STRCMP(name + 1, "ALL") == 0
6013 || STRCMP(name + 1, "TOP") == 0
6014 || STRCMP(name + 1, "CONTAINED") == 0)
6015 {
6016 if (TOUPPER_ASC(**arg) != 'C')
6017 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006018 semsg(_("E407: %s not allowed here"), name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006019 failed = TRUE;
6020 vim_free(name);
6021 break;
6022 }
6023 if (count != 0)
6024 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006025 semsg(_("E408: %s must be first in contains list"),
Bram Moolenaard7a96152017-01-22 15:28:55 +01006026 name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006027 failed = TRUE;
6028 vim_free(name);
6029 break;
6030 }
6031 if (name[1] == 'A')
6032 id = SYNID_ALLBUT;
6033 else if (name[1] == 'T')
6034 id = SYNID_TOP;
6035 else
6036 id = SYNID_CONTAINED;
6037 id += current_syn_inc_tag;
6038 }
6039 else if (name[1] == '@')
6040 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01006041 if (skip)
6042 id = -1;
6043 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01006044 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006045 }
6046 else
6047 {
6048 /*
6049 * Handle full group name.
6050 */
6051 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6052 id = syn_check_group(name + 1, (int)(end - p));
6053 else
6054 {
6055 /*
6056 * Handle match of regexp with group names.
6057 */
6058 *name = '^';
6059 STRCAT(name, "$");
6060 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6061 if (regmatch.regprog == NULL)
6062 {
6063 failed = TRUE;
6064 vim_free(name);
6065 break;
6066 }
6067
6068 regmatch.rm_ic = TRUE;
6069 id = 0;
6070 for (i = highlight_ga.ga_len; --i >= 0; )
6071 {
6072 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6073 (colnr_T)0))
6074 {
6075 if (round == 2)
6076 {
6077 /* Got more items than expected; can happen
6078 * when adding items that match:
6079 * "contains=a.*b,axb".
6080 * Go back to first round */
6081 if (count >= total_count)
6082 {
6083 vim_free(retval);
6084 round = 1;
6085 }
6086 else
6087 retval[count] = i + 1;
6088 }
6089 ++count;
6090 id = -1; /* remember that we found one */
6091 }
6092 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006093 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006094 }
6095 }
6096 vim_free(name);
6097 if (id == 0)
6098 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006099 semsg(_("E409: Unknown group name: %s"), p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006100 failed = TRUE;
6101 break;
6102 }
6103 if (id > 0)
6104 {
6105 if (round == 2)
6106 {
6107 /* Got more items than expected, go back to first round */
6108 if (count >= total_count)
6109 {
6110 vim_free(retval);
6111 round = 1;
6112 }
6113 else
6114 retval[count] = id;
6115 }
6116 ++count;
6117 }
6118 p = skipwhite(end);
6119 if (*p != ',')
6120 break;
6121 p = skipwhite(p + 1); /* skip comma in between arguments */
6122 }
6123 if (failed)
6124 break;
6125 if (round == 1)
6126 {
6127 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6128 if (retval == NULL)
6129 break;
6130 retval[count] = 0; /* zero means end of the list */
6131 total_count = count;
6132 }
6133 }
6134
6135 *arg = p;
6136 if (failed || retval == NULL)
6137 {
6138 vim_free(retval);
6139 return FAIL;
6140 }
6141
6142 if (*list == NULL)
6143 *list = retval;
6144 else
6145 vim_free(retval); /* list already found, don't overwrite it */
6146
6147 return OK;
6148}
6149
6150/*
6151 * Make a copy of an ID list.
6152 */
6153 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006154copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006155{
6156 int len;
6157 int count;
6158 short *retval;
6159
6160 if (list == NULL)
6161 return NULL;
6162
6163 for (count = 0; list[count]; ++count)
6164 ;
6165 len = (count + 1) * sizeof(short);
6166 retval = (short *)alloc((unsigned)len);
6167 if (retval != NULL)
6168 mch_memmove(retval, list, (size_t)len);
6169
6170 return retval;
6171}
6172
6173/*
6174 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6175 * "cur_si" can be NULL if not checking the "containedin" list.
6176 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6177 * the current item.
6178 * This function is called very often, keep it fast!!
6179 */
6180 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006181in_id_list(
6182 stateitem_T *cur_si, /* current item or NULL */
6183 short *list, /* id list */
6184 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6185 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006186{
6187 int retval;
6188 short *scl_list;
6189 short item;
6190 short id = ssp->id;
6191 static int depth = 0;
6192 int r;
6193
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006194 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006195 if (cur_si != NULL && ssp->cont_in_list != NULL
6196 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006197 {
6198 /* Ignore transparent items without a contains argument. Double check
6199 * that we don't go back past the first one. */
6200 while ((cur_si->si_flags & HL_TRANS_CONT)
6201 && cur_si > (stateitem_T *)(current_state.ga_data))
6202 --cur_si;
6203 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6204 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006205 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6206 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006207 return TRUE;
6208 }
6209
6210 if (list == NULL)
6211 return FALSE;
6212
6213 /*
6214 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6215 * inside anything. Only allow not-contained groups.
6216 */
6217 if (list == ID_LIST_ALL)
6218 return !contained;
6219
6220 /*
6221 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6222 * contains list. We also require that "id" is at the same ":syn include"
6223 * level as the list.
6224 */
6225 item = *list;
6226 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6227 {
6228 if (item < SYNID_TOP)
6229 {
6230 /* ALL or ALLBUT: accept all groups in the same file */
6231 if (item - SYNID_ALLBUT != ssp->inc_tag)
6232 return FALSE;
6233 }
6234 else if (item < SYNID_CONTAINED)
6235 {
6236 /* TOP: accept all not-contained groups in the same file */
6237 if (item - SYNID_TOP != ssp->inc_tag || contained)
6238 return FALSE;
6239 }
6240 else
6241 {
6242 /* CONTAINED: accept all contained groups in the same file */
6243 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6244 return FALSE;
6245 }
6246 item = *++list;
6247 retval = FALSE;
6248 }
6249 else
6250 retval = TRUE;
6251
6252 /*
6253 * Return "retval" if id is in the contains list.
6254 */
6255 while (item != 0)
6256 {
6257 if (item == id)
6258 return retval;
6259 if (item >= SYNID_CLUSTER)
6260 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006261 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006262 /* restrict recursiveness to 30 to avoid an endless loop for a
6263 * cluster that includes itself (indirectly) */
6264 if (scl_list != NULL && depth < 30)
6265 {
6266 ++depth;
6267 r = in_id_list(NULL, scl_list, ssp, contained);
6268 --depth;
6269 if (r)
6270 return retval;
6271 }
6272 }
6273 item = *++list;
6274 }
6275 return !retval;
6276}
6277
6278struct subcommand
6279{
Bram Moolenaard99df422016-01-29 23:20:40 +01006280 char *name; /* subcommand name */
6281 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006282};
6283
6284static struct subcommand subcommands[] =
6285{
6286 {"case", syn_cmd_case},
6287 {"clear", syn_cmd_clear},
6288 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006289 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006290 {"enable", syn_cmd_enable},
6291 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006292 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006293 {"keyword", syn_cmd_keyword},
6294 {"list", syn_cmd_list},
6295 {"manual", syn_cmd_manual},
6296 {"match", syn_cmd_match},
6297 {"on", syn_cmd_on},
6298 {"off", syn_cmd_off},
6299 {"region", syn_cmd_region},
6300 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006301 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006302 {"sync", syn_cmd_sync},
6303 {"", syn_cmd_list},
6304 {NULL, NULL}
6305};
6306
6307/*
6308 * ":syntax".
6309 * This searches the subcommands[] table for the subcommand name, and calls a
6310 * syntax_subcommand() function to do the rest.
6311 */
6312 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006313ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006314{
6315 char_u *arg = eap->arg;
6316 char_u *subcmd_end;
6317 char_u *subcmd_name;
6318 int i;
6319
6320 syn_cmdlinep = eap->cmdlinep;
6321
6322 /* isolate subcommand name */
6323 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6324 ;
6325 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6326 if (subcmd_name != NULL)
6327 {
6328 if (eap->skip) /* skip error messages for all subcommands */
6329 ++emsg_skip;
6330 for (i = 0; ; ++i)
6331 {
6332 if (subcommands[i].name == NULL)
6333 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006334 semsg(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006335 break;
6336 }
6337 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6338 {
6339 eap->arg = skipwhite(subcmd_end);
6340 (subcommands[i].func)(eap, FALSE);
6341 break;
6342 }
6343 }
6344 vim_free(subcmd_name);
6345 if (eap->skip)
6346 --emsg_skip;
6347 }
6348}
6349
Bram Moolenaar860cae12010-06-05 23:22:07 +02006350 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006351ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006352{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006353 char_u *old_value;
6354 char_u *new_value;
6355
Bram Moolenaar860cae12010-06-05 23:22:07 +02006356 if (curwin->w_s == &curwin->w_buffer->b_s)
6357 {
6358 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6359 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006360 hash_init(&curwin->w_s->b_keywtab);
6361 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006362#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006363 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006364 curwin->w_p_spell = FALSE; /* No spell checking */
6365 clear_string_option(&curwin->w_s->b_p_spc);
6366 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006367 clear_string_option(&curwin->w_s->b_p_spl);
6368#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006369 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006370 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006371
6372 /* save value of b:current_syntax */
6373 old_value = get_var_value((char_u *)"b:current_syntax");
6374 if (old_value != NULL)
6375 old_value = vim_strsave(old_value);
6376
6377 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6378 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006379 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006380
6381 /* move value of b:current_syntax to w:current_syntax */
6382 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006383 if (new_value != NULL)
6384 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006385
6386 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006387 if (old_value == NULL)
6388 do_unlet((char_u *)"b:current_syntax", TRUE);
6389 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006390 {
6391 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6392 vim_free(old_value);
6393 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006394}
6395
6396 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006397syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006398{
6399 return (win->w_s->b_syn_patterns.ga_len != 0
6400 || win->w_s->b_syn_clusters.ga_len != 0
6401 || win->w_s->b_keywtab.ht_used > 0
6402 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006403}
6404
6405#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6406
6407static enum
6408{
6409 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006410 EXP_CASE, /* expand ":syn case" arguments */
6411 EXP_SPELL, /* expand ":syn spell" arguments */
6412 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006413} expand_what;
6414
Bram Moolenaar4f688582007-07-24 12:34:30 +00006415/*
6416 * Reset include_link, include_default, include_none to 0.
6417 * Called when we are done expanding.
6418 */
6419 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006420reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006421{
6422 include_link = include_default = include_none = 0;
6423}
6424
6425/*
6426 * Handle command line completion for :match and :echohl command: Add "None"
6427 * as highlight group.
6428 */
6429 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006430set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006431{
6432 xp->xp_context = EXPAND_HIGHLIGHT;
6433 xp->xp_pattern = arg;
6434 include_none = 1;
6435}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006436
6437/*
6438 * Handle command line completion for :syntax command.
6439 */
6440 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006441set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006442{
6443 char_u *p;
6444
6445 /* Default: expand subcommands */
6446 xp->xp_context = EXPAND_SYNTAX;
6447 expand_what = EXP_SUBCMD;
6448 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006449 include_link = 0;
6450 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006451
6452 /* (part of) subcommand already typed */
6453 if (*arg != NUL)
6454 {
6455 p = skiptowhite(arg);
6456 if (*p != NUL) /* past first word */
6457 {
6458 xp->xp_pattern = skipwhite(p);
6459 if (*skiptowhite(xp->xp_pattern) != NUL)
6460 xp->xp_context = EXPAND_NOTHING;
6461 else if (STRNICMP(arg, "case", p - arg) == 0)
6462 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006463 else if (STRNICMP(arg, "spell", p - arg) == 0)
6464 expand_what = EXP_SPELL;
6465 else if (STRNICMP(arg, "sync", p - arg) == 0)
6466 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006467 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6468 || STRNICMP(arg, "region", p - arg) == 0
6469 || STRNICMP(arg, "match", p - arg) == 0
6470 || STRNICMP(arg, "list", p - arg) == 0)
6471 xp->xp_context = EXPAND_HIGHLIGHT;
6472 else
6473 xp->xp_context = EXPAND_NOTHING;
6474 }
6475 }
6476}
6477
Bram Moolenaar071d4272004-06-13 20:20:40 +00006478/*
6479 * Function given to ExpandGeneric() to obtain the list syntax names for
6480 * expansion.
6481 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006482 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006483get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006484{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006485 switch (expand_what)
6486 {
6487 case EXP_SUBCMD:
6488 return (char_u *)subcommands[idx].name;
6489 case EXP_CASE:
6490 {
6491 static char *case_args[] = {"match", "ignore", NULL};
6492 return (char_u *)case_args[idx];
6493 }
6494 case EXP_SPELL:
6495 {
6496 static char *spell_args[] =
6497 {"toplevel", "notoplevel", "default", NULL};
6498 return (char_u *)spell_args[idx];
6499 }
6500 case EXP_SYNC:
6501 {
6502 static char *sync_args[] =
6503 {"ccomment", "clear", "fromstart",
6504 "linebreaks=", "linecont", "lines=", "match",
6505 "maxlines=", "minlines=", "region", NULL};
6506 return (char_u *)sync_args[idx];
6507 }
6508 }
6509 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006510}
6511
6512#endif /* FEAT_CMDL_COMPL */
6513
Bram Moolenaar071d4272004-06-13 20:20:40 +00006514/*
6515 * Function called for expression evaluation: get syntax ID at file position.
6516 */
6517 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006518syn_get_id(
6519 win_T *wp,
6520 long lnum,
6521 colnr_T col,
6522 int trans, /* remove transparency */
6523 int *spellp, /* return: can do spell checking */
6524 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006525{
6526 /* When the position is not after the current position and in the same
6527 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006528 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006529 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006530 || col < current_col)
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006531 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006532 else if (wp->w_buffer == syn_buf
6533 && lnum == current_lnum
6534 && col > current_col)
6535 /* next_match may not be correct when moving around, e.g. with the
6536 * "skip" expression in searchpair() */
6537 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006538
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006539 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006540
6541 return (trans ? current_trans_id : current_id);
6542}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006543
Bram Moolenaar860cae12010-06-05 23:22:07 +02006544#if defined(FEAT_CONCEAL) || defined(PROTO)
6545/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006546 * Get extra information about the syntax item. Must be called right after
6547 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006548 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006549 * Returns the current flags.
6550 */
6551 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006552get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006553{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006554 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006555 return current_flags;
6556}
6557
6558/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006559 * Return conceal substitution character
6560 */
6561 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006562syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006563{
6564 return current_sub_char;
6565}
6566#endif
6567
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006568#if defined(FEAT_EVAL) || defined(PROTO)
6569/*
6570 * Return the syntax ID at position "i" in the current stack.
6571 * The caller must have called syn_get_id() before to fill the stack.
6572 * Returns -1 when "i" is out of range.
6573 */
6574 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006575syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006576{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006577 if (i >= current_state.ga_len)
6578 {
6579 /* Need to invalidate the state, because we didn't properly finish it
6580 * for the last character, "keep_state" was TRUE. */
6581 invalidate_current_state();
6582 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006583 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006584 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006585 return CUR_STATE(i).si_id;
6586}
6587#endif
6588
Bram Moolenaar071d4272004-06-13 20:20:40 +00006589#if defined(FEAT_FOLDING) || defined(PROTO)
6590/*
6591 * Function called to get folding level for line "lnum" in window "wp".
6592 */
6593 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006594syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006595{
6596 int level = 0;
6597 int i;
6598
6599 /* Return quickly when there are no fold items at all. */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006600 if (wp->w_s->b_syn_folditems != 0
6601 && !wp->w_s->b_syn_error
6602# ifdef SYN_TIME_LIMIT
6603 && !wp->w_s->b_syn_slow
6604# endif
6605 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006606 {
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006607 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006608
6609 for (i = 0; i < current_state.ga_len; ++i)
6610 if (CUR_STATE(i).si_flags & HL_FOLD)
6611 ++level;
6612 }
6613 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006614 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006615 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006616 if (level < 0)
6617 level = 0;
6618 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006619 return level;
6620}
6621#endif
6622
Bram Moolenaar01615492015-02-03 13:00:38 +01006623#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006624/*
6625 * ":syntime".
6626 */
6627 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006628ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006629{
6630 if (STRCMP(eap->arg, "on") == 0)
6631 syn_time_on = TRUE;
6632 else if (STRCMP(eap->arg, "off") == 0)
6633 syn_time_on = FALSE;
6634 else if (STRCMP(eap->arg, "clear") == 0)
6635 syntime_clear();
6636 else if (STRCMP(eap->arg, "report") == 0)
6637 syntime_report();
6638 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006639 semsg(_(e_invarg2), eap->arg);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006640}
6641
6642 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006643syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006644{
6645 profile_zero(&st->total);
6646 profile_zero(&st->slowest);
6647 st->count = 0;
6648 st->match = 0;
6649}
6650
6651/*
6652 * Clear the syntax timing for the current buffer.
6653 */
6654 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006655syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006656{
6657 int idx;
6658 synpat_T *spp;
6659
6660 if (!syntax_present(curwin))
6661 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006662 msg(_(msg_no_items));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006663 return;
6664 }
6665 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6666 {
6667 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6668 syn_clear_time(&spp->sp_time);
6669 }
6670}
6671
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006672#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6673/*
6674 * Function given to ExpandGeneric() to obtain the possible arguments of the
6675 * ":syntime {on,off,clear,report}" command.
6676 */
6677 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006678get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006679{
6680 switch (idx)
6681 {
6682 case 0: return (char_u *)"on";
6683 case 1: return (char_u *)"off";
6684 case 2: return (char_u *)"clear";
6685 case 3: return (char_u *)"report";
6686 }
6687 return NULL;
6688}
6689#endif
6690
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006691typedef struct
6692{
6693 proftime_T total;
6694 int count;
6695 int match;
6696 proftime_T slowest;
6697 proftime_T average;
6698 int id;
6699 char_u *pattern;
6700} time_entry_T;
6701
6702 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006703syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006704{
6705 const time_entry_T *s1 = v1;
6706 const time_entry_T *s2 = v2;
6707
6708 return profile_cmp(&s1->total, &s2->total);
6709}
6710
6711/*
6712 * Clear the syntax timing for the current buffer.
6713 */
6714 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006715syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006716{
6717 int idx;
6718 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006719# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006720 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006721# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006722 int len;
6723 proftime_T total_total;
6724 int total_count = 0;
6725 garray_T ga;
6726 time_entry_T *p;
6727
6728 if (!syntax_present(curwin))
6729 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006730 msg(_(msg_no_items));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006731 return;
6732 }
6733
6734 ga_init2(&ga, sizeof(time_entry_T), 50);
6735 profile_zero(&total_total);
6736 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6737 {
6738 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6739 if (spp->sp_time.count > 0)
6740 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006741 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006742 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6743 p->total = spp->sp_time.total;
6744 profile_add(&total_total, &spp->sp_time.total);
6745 p->count = spp->sp_time.count;
6746 p->match = spp->sp_time.match;
6747 total_count += spp->sp_time.count;
6748 p->slowest = spp->sp_time.slowest;
6749# ifdef FEAT_FLOAT
6750 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6751 p->average = tm;
6752# endif
6753 p->id = spp->sp_syn.id;
6754 p->pattern = spp->sp_pattern;
6755 ++ga.ga_len;
6756 }
6757 }
6758
Bram Moolenaara2162552017-01-08 17:46:20 +01006759 /* Sort on total time. Skip if there are no items to avoid passing NULL
6760 * pointer to qsort(). */
6761 if (ga.ga_len > 1)
6762 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006763 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006764
Bram Moolenaar32526b32019-01-19 17:43:09 +01006765 msg_puts_title(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6766 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006767 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6768 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006769 p = ((time_entry_T *)ga.ga_data) + idx;
6770
Bram Moolenaar32526b32019-01-19 17:43:09 +01006771 msg_puts(profile_msg(&p->total));
6772 msg_puts(" "); /* make sure there is always a separating space */
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006773 msg_advance(13);
6774 msg_outnum(p->count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006775 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006776 msg_advance(20);
6777 msg_outnum(p->match);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006778 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006779 msg_advance(26);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006780 msg_puts(profile_msg(&p->slowest));
6781 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006782 msg_advance(38);
6783# ifdef FEAT_FLOAT
Bram Moolenaar32526b32019-01-19 17:43:09 +01006784 msg_puts(profile_msg(&p->average));
6785 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006786# endif
6787 msg_advance(50);
6788 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006789 msg_puts(" ");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006790
6791 msg_advance(69);
6792 if (Columns < 80)
6793 len = 20; /* will wrap anyway */
6794 else
6795 len = Columns - 70;
6796 if (len > (int)STRLEN(p->pattern))
6797 len = (int)STRLEN(p->pattern);
6798 msg_outtrans_len(p->pattern, len);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006799 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006800 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006801 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006802 if (!got_int)
6803 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01006804 msg_puts("\n");
6805 msg_puts(profile_msg(&total_total));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006806 msg_advance(13);
6807 msg_outnum(total_count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01006808 msg_puts("\n");
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006809 }
6810}
6811#endif
6812
Bram Moolenaar071d4272004-06-13 20:20:40 +00006813#endif /* FEAT_SYN_HL */
6814
Bram Moolenaar071d4272004-06-13 20:20:40 +00006815/**************************************
6816 * Highlighting stuff *
6817 **************************************/
6818
6819/*
6820 * The default highlight groups. These are compiled-in for fast startup and
6821 * they still work when the runtime files can't be found.
6822 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006823 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6824 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006825 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006826#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006827# define CENT(a, b) b
6828#else
6829# define CENT(a, b) a
6830#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006831static char *(highlight_init_both[]) = {
6832 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6833 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6834 CENT("IncSearch term=reverse cterm=reverse",
6835 "IncSearch term=reverse cterm=reverse gui=reverse"),
6836 CENT("ModeMsg term=bold cterm=bold",
6837 "ModeMsg term=bold cterm=bold gui=bold"),
6838 CENT("NonText term=bold ctermfg=Blue",
6839 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6840 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6841 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6842 CENT("StatusLineNC term=reverse cterm=reverse",
6843 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
6844 "default link EndOfBuffer NonText",
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006845 CENT("VertSplit term=reverse cterm=reverse",
6846 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006847#ifdef FEAT_CLIPBOARD
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006848 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6849 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006850#endif
6851#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006852 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6853 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006854#endif
6855#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006856 CENT("PmenuSbar ctermbg=Grey",
6857 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006858#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006859 CENT("TabLineSel term=bold cterm=bold",
6860 "TabLineSel term=bold cterm=bold gui=bold"),
6861 CENT("TabLineFill term=reverse cterm=reverse",
6862 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00006863#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006864 "Cursor guibg=fg guifg=bg",
6865 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006866#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006867 "default link QuickFixLine Search",
Bram Moolenaarf90b6e02019-05-09 19:26:38 +02006868 CENT("Normal cterm=NONE", "Normal gui=NONE"),
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006869 NULL
6870};
Bram Moolenaar071d4272004-06-13 20:20:40 +00006871
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006872/* Default colors only used with a light background. */
6873static char *(highlight_init_light[]) = {
6874 CENT("Directory term=bold ctermfg=DarkBlue",
6875 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6876 CENT("LineNr term=underline ctermfg=Brown",
6877 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6878 CENT("CursorLineNr term=bold ctermfg=Brown",
6879 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
6880 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6881 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6882 CENT("Question term=standout ctermfg=DarkGreen",
6883 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6884 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6885 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006886#ifdef FEAT_SPELL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006887 CENT("SpellBad term=reverse ctermbg=LightRed",
6888 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6889 CENT("SpellCap term=reverse ctermbg=LightBlue",
6890 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6891 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6892 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6893 CENT("SpellLocal term=underline ctermbg=Cyan",
6894 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006895#endif
6896#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006897 CENT("PmenuThumb ctermbg=Black",
6898 "PmenuThumb ctermbg=Black guibg=Black"),
6899 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6900 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6901 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6902 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006903#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006904 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6905 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6906 CENT("Title term=bold ctermfg=DarkMagenta",
6907 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6908 CENT("WarningMsg term=standout ctermfg=DarkRed",
6909 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006910#ifdef FEAT_WILDMENU
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006911 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6912 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006913#endif
6914#ifdef FEAT_FOLDING
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006915 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6916 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6917 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6918 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006919#endif
6920#ifdef FEAT_SIGNS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006921 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6922 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006923#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006924 CENT("Visual term=reverse",
6925 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006926#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006927 CENT("DiffAdd term=bold ctermbg=LightBlue",
6928 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6929 CENT("DiffChange term=bold ctermbg=LightMagenta",
6930 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6931 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6932 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006933#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006934 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6935 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006936#ifdef FEAT_SYN_HL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006937 CENT("CursorColumn term=reverse ctermbg=LightGrey",
6938 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
6939 CENT("CursorLine term=underline cterm=underline",
6940 "CursorLine term=underline cterm=underline guibg=Grey90"),
6941 CENT("ColorColumn term=reverse ctermbg=LightRed",
6942 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006943#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006944#ifdef FEAT_CONCEAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006945 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6946 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02006947#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006948 CENT("MatchParen term=reverse ctermbg=Cyan",
6949 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006950#ifdef FEAT_TERMINAL
6951 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
6952 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
6953 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
6954 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
6955#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02006956#ifdef FEAT_MENU
6957 CENT("ToolbarLine term=underline ctermbg=LightGrey",
6958 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
6959 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006960 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02006961#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006962 NULL
6963};
Bram Moolenaar071d4272004-06-13 20:20:40 +00006964
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006965/* Default colors only used with a dark background. */
6966static char *(highlight_init_dark[]) = {
6967 CENT("Directory term=bold ctermfg=LightCyan",
6968 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6969 CENT("LineNr term=underline ctermfg=Yellow",
6970 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6971 CENT("CursorLineNr term=bold ctermfg=Yellow",
6972 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
6973 CENT("MoreMsg term=bold ctermfg=LightGreen",
6974 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6975 CENT("Question term=standout ctermfg=LightGreen",
6976 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6977 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6978 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6979 CENT("SpecialKey term=bold ctermfg=LightBlue",
6980 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006981#ifdef FEAT_SPELL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006982 CENT("SpellBad term=reverse ctermbg=Red",
6983 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6984 CENT("SpellCap term=reverse ctermbg=Blue",
6985 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6986 CENT("SpellRare term=reverse ctermbg=Magenta",
6987 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6988 CENT("SpellLocal term=underline ctermbg=Cyan",
6989 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006990#endif
6991#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006992 CENT("PmenuThumb ctermbg=White",
6993 "PmenuThumb ctermbg=White guibg=White"),
6994 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
6995 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
6996 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
6997 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006998#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006999 CENT("Title term=bold ctermfg=LightMagenta",
7000 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
7001 CENT("WarningMsg term=standout ctermfg=LightRed",
7002 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007003#ifdef FEAT_WILDMENU
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007004 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
7005 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007006#endif
7007#ifdef FEAT_FOLDING
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007008 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
7009 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
7010 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7011 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007012#endif
7013#ifdef FEAT_SIGNS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007014 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7015 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007016#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007017 CENT("Visual term=reverse",
7018 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007019#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007020 CENT("DiffAdd term=bold ctermbg=DarkBlue",
7021 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
7022 CENT("DiffChange term=bold ctermbg=DarkMagenta",
7023 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
7024 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
7025 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007026#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007027 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7028 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007029#ifdef FEAT_SYN_HL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007030 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
7031 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
7032 CENT("CursorLine term=underline cterm=underline",
7033 "CursorLine term=underline cterm=underline guibg=Grey40"),
7034 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7035 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007036#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007037 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7038 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02007039#ifdef FEAT_CONCEAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007040 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7041 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02007042#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007043#ifdef FEAT_TERMINAL
7044 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
7045 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
7046 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
7047 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
7048#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007049#ifdef FEAT_MENU
7050 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02007051 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007052 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
7053 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
7054#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007055 NULL
7056};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007057
7058 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007059init_highlight(
7060 int both, /* include groups where 'bg' doesn't matter */
7061 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007062{
7063 int i;
7064 char **pp;
7065 static int had_both = FALSE;
7066#ifdef FEAT_EVAL
7067 char_u *p;
7068
7069 /*
7070 * Try finding the color scheme file. Used when a color file was loaded
7071 * and 'background' or 't_Co' is changed.
7072 */
7073 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007074 if (p != NULL)
7075 {
7076 /* The value of g:colors_name could be freed when sourcing the script,
7077 * making "p" invalid, so copy it. */
7078 char_u *copy_p = vim_strsave(p);
7079 int r;
7080
7081 if (copy_p != NULL)
7082 {
7083 r = load_colors(copy_p);
7084 vim_free(copy_p);
7085 if (r == OK)
7086 return;
7087 }
7088 }
7089
Bram Moolenaar071d4272004-06-13 20:20:40 +00007090#endif
7091
7092 /*
7093 * Didn't use a color file, use the compiled-in colors.
7094 */
7095 if (both)
7096 {
7097 had_both = TRUE;
7098 pp = highlight_init_both;
7099 for (i = 0; pp[i] != NULL; ++i)
7100 do_highlight((char_u *)pp[i], reset, TRUE);
7101 }
7102 else if (!had_both)
7103 /* Don't do anything before the call with both == TRUE from main().
7104 * Not everything has been setup then, and that call will overrule
7105 * everything anyway. */
7106 return;
7107
7108 if (*p_bg == 'l')
7109 pp = highlight_init_light;
7110 else
7111 pp = highlight_init_dark;
7112 for (i = 0; pp[i] != NULL; ++i)
7113 do_highlight((char_u *)pp[i], reset, TRUE);
7114
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007115 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007116 * depend on the number of colors available.
7117 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007118 * to avoid Statement highlighted text disappears.
7119 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007120 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007121 do_highlight((char_u *)(*p_bg == 'l'
7122 ? "Visual cterm=NONE ctermbg=LightGrey"
7123 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007124 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007125 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007126 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7127 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007128 if (*p_bg == 'l')
7129 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7130 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007131
Bram Moolenaar071d4272004-06-13 20:20:40 +00007132#ifdef FEAT_SYN_HL
7133 /*
7134 * If syntax highlighting is enabled load the highlighting for it.
7135 */
7136 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007137 {
7138 static int recursive = 0;
7139
7140 if (recursive >= 5)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007141 emsg(_("E679: recursive loop loading syncolor.vim"));
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007142 else
7143 {
7144 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007145 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007146 --recursive;
7147 }
7148 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007149#endif
7150}
7151
7152/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007153 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007154 * Return OK for success, FAIL for failure.
7155 */
7156 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007157load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007158{
7159 char_u *buf;
7160 int retval = FAIL;
7161 static int recursive = FALSE;
7162
7163 /* When being called recursively, this is probably because setting
7164 * 'background' caused the highlighting to be reloaded. This means it is
7165 * working, thus we should return OK. */
7166 if (recursive)
7167 return OK;
7168
7169 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007170 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007171 if (buf != NULL)
7172 {
Bram Moolenaar60a68362018-04-30 15:40:48 +02007173 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
7174 curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007175 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007176 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007177 vim_free(buf);
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007178 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007179 }
7180 recursive = FALSE;
7181
7182 return retval;
7183}
7184
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007185static char *(color_names[28]) = {
7186 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7187 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7188 "Gray", "Grey", "LightGray", "LightGrey",
7189 "DarkGray", "DarkGrey",
7190 "Blue", "LightBlue", "Green", "LightGreen",
7191 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7192 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7193 /* indices:
7194 * 0, 1, 2, 3,
7195 * 4, 5, 6, 7,
7196 * 8, 9, 10, 11,
7197 * 12, 13,
7198 * 14, 15, 16, 17,
7199 * 18, 19, 20, 21, 22,
7200 * 23, 24, 25, 26, 27 */
7201static int color_numbers_16[28] = {0, 1, 2, 3,
7202 4, 5, 6, 6,
7203 7, 7, 7, 7,
7204 8, 8,
7205 9, 9, 10, 10,
7206 11, 11, 12, 12, 13,
7207 13, 14, 14, 15, -1};
7208/* for xterm with 88 colors... */
7209static int color_numbers_88[28] = {0, 4, 2, 6,
7210 1, 5, 32, 72,
7211 84, 84, 7, 7,
7212 82, 82,
7213 12, 43, 10, 61,
7214 14, 63, 9, 74, 13,
7215 75, 11, 78, 15, -1};
7216/* for xterm with 256 colors... */
7217static int color_numbers_256[28] = {0, 4, 2, 6,
7218 1, 5, 130, 130,
7219 248, 248, 7, 7,
7220 242, 242,
7221 12, 81, 10, 121,
7222 14, 159, 9, 224, 13,
7223 225, 11, 229, 15, -1};
7224/* for terminals with less than 16 colors... */
7225static int color_numbers_8[28] = {0, 4, 2, 6,
7226 1, 5, 3, 3,
7227 7, 7, 7, 7,
7228 0+8, 0+8,
7229 4+8, 4+8, 2+8, 2+8,
7230 6+8, 6+8, 1+8, 1+8, 5+8,
7231 5+8, 3+8, 3+8, 7+8, -1};
7232
7233/*
7234 * Lookup the "cterm" value to be used for color with index "idx" in
7235 * color_names[].
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007236 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
7237 * colors, otherwise it will be unchanged.
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007238 */
7239 int
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007240lookup_color(int idx, int foreground, int *boldp)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007241{
7242 int color = color_numbers_16[idx];
7243 char_u *p;
7244
7245 /* Use the _16 table to check if it's a valid color name. */
7246 if (color < 0)
7247 return -1;
7248
7249 if (t_colors == 8)
7250 {
7251 /* t_Co is 8: use the 8 colors table */
7252#if defined(__QNXNTO__)
7253 color = color_numbers_8_qansi[idx];
7254#else
7255 color = color_numbers_8[idx];
7256#endif
7257 if (foreground)
7258 {
7259 /* set/reset bold attribute to get light foreground
7260 * colors (on some terminals, e.g. "linux") */
7261 if (color & 8)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007262 *boldp = TRUE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007263 else
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007264 *boldp = FALSE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007265 }
7266 color &= 7; /* truncate to 8 colors */
7267 }
7268 else if (t_colors == 16 || t_colors == 88
7269 || t_colors >= 256)
7270 {
7271 /*
7272 * Guess: if the termcap entry ends in 'm', it is
7273 * probably an xterm-like terminal. Use the changed
7274 * order for colors.
7275 */
7276 if (*T_CAF != NUL)
7277 p = T_CAF;
7278 else
7279 p = T_CSF;
7280 if (*p != NUL && (t_colors > 256
7281 || *(p + STRLEN(p) - 1) == 'm'))
7282 {
7283 if (t_colors == 88)
7284 color = color_numbers_88[idx];
7285 else if (t_colors >= 256)
7286 color = color_numbers_256[idx];
7287 else
7288 color = color_numbers_8[idx];
7289 }
Bram Moolenaarc9026092017-10-04 19:35:02 +02007290#ifdef FEAT_TERMRESPONSE
Bram Moolenaara0a6f272017-10-04 18:04:16 +02007291 if (t_colors >= 256 && color == 15 && is_mac_terminal)
7292 /* Terminal.app has a bug: 15 is light grey. Use white
7293 * from the color cube instead. */
7294 color = 231;
Bram Moolenaarc9026092017-10-04 19:35:02 +02007295#endif
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007296 }
7297 return color;
7298}
7299
Bram Moolenaar071d4272004-06-13 20:20:40 +00007300/*
7301 * Handle the ":highlight .." command.
7302 * When using ":hi clear" this is called recursively for each group with
7303 * "forceit" and "init" both TRUE.
7304 */
7305 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007306do_highlight(
7307 char_u *line,
7308 int forceit,
7309 int init) /* TRUE when called for initializing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007310{
7311 char_u *name_end;
7312 char_u *p;
7313 char_u *linep;
7314 char_u *key_start;
7315 char_u *arg_start;
7316 char_u *key = NULL, *arg = NULL;
7317 long i;
7318 int off;
7319 int len;
7320 int attr;
7321 int id;
7322 int idx;
Bram Moolenaar99433292017-09-08 12:37:47 +02007323 struct hl_group item_before;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007324 int did_change = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007325 int dodefault = FALSE;
7326 int doclear = FALSE;
7327 int dolink = FALSE;
7328 int error = FALSE;
7329 int color;
7330 int is_normal_group = FALSE; /* "Normal" group */
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01007331#ifdef FEAT_TERMINAL
7332 int is_terminal_group = FALSE; /* "Terminal" group */
7333#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007334#ifdef FEAT_GUI_X11
7335 int is_menu_group = FALSE; /* "Menu" group */
7336 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7337 int is_tooltip_group = FALSE; /* "Tooltip" group */
7338 int do_colors = FALSE; /* need to update colors? */
7339#else
7340# define is_menu_group 0
7341# define is_tooltip_group 0
7342#endif
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007343#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
7344 int did_highlight_changed = FALSE;
7345#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007346
7347 /*
7348 * If no argument, list current highlighting.
7349 */
7350 if (ends_excmd(*line))
7351 {
7352 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7353 /* TODO: only call when the group has attributes set */
7354 highlight_list_one((int)i);
7355 return;
7356 }
7357
7358 /*
7359 * Isolate the name.
7360 */
7361 name_end = skiptowhite(line);
7362 linep = skipwhite(name_end);
7363
7364 /*
7365 * Check for "default" argument.
7366 */
7367 if (STRNCMP(line, "default", name_end - line) == 0)
7368 {
7369 dodefault = TRUE;
7370 line = linep;
7371 name_end = skiptowhite(line);
7372 linep = skipwhite(name_end);
7373 }
7374
7375 /*
7376 * Check for "clear" or "link" argument.
7377 */
7378 if (STRNCMP(line, "clear", name_end - line) == 0)
7379 doclear = TRUE;
7380 if (STRNCMP(line, "link", name_end - line) == 0)
7381 dolink = TRUE;
7382
7383 /*
7384 * ":highlight {group-name}": list highlighting for one group.
7385 */
7386 if (!doclear && !dolink && ends_excmd(*linep))
7387 {
7388 id = syn_namen2id(line, (int)(name_end - line));
7389 if (id == 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007390 semsg(_("E411: highlight group not found: %s"), line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007391 else
7392 highlight_list_one(id);
7393 return;
7394 }
7395
7396 /*
7397 * Handle ":highlight link {from} {to}" command.
7398 */
7399 if (dolink)
7400 {
7401 char_u *from_start = linep;
7402 char_u *from_end;
7403 char_u *to_start;
7404 char_u *to_end;
7405 int from_id;
7406 int to_id;
7407
7408 from_end = skiptowhite(from_start);
7409 to_start = skipwhite(from_end);
7410 to_end = skiptowhite(to_start);
7411
7412 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7413 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007414 semsg(_("E412: Not enough arguments: \":highlight link %s\""),
Bram Moolenaar071d4272004-06-13 20:20:40 +00007415 from_start);
7416 return;
7417 }
7418
7419 if (!ends_excmd(*skipwhite(to_end)))
7420 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007421 semsg(_("E413: Too many arguments: \":highlight link %s\""), from_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007422 return;
7423 }
7424
7425 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7426 if (STRNCMP(to_start, "NONE", 4) == 0)
7427 to_id = 0;
7428 else
7429 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7430
Bram Moolenaar414168d2017-09-10 15:21:55 +02007431 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007432 {
7433 /*
7434 * Don't allow a link when there already is some highlighting
7435 * for the group, unless '!' is used
7436 */
7437 if (to_id > 0 && !forceit && !init
7438 && hl_has_settings(from_id - 1, dodefault))
7439 {
7440 if (sourcing_name == NULL && !dodefault)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007441 emsg(_("E414: group has settings, highlight link ignored"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007442 }
Bram Moolenaar414168d2017-09-10 15:21:55 +02007443 else if (HL_TABLE()[from_id - 1].sg_link != to_id
Bram Moolenaar99433292017-09-08 12:37:47 +02007444#ifdef FEAT_EVAL
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02007445 || HL_TABLE()[from_id - 1].sg_script_ctx.sc_sid
7446 != current_sctx.sc_sid
Bram Moolenaar99433292017-09-08 12:37:47 +02007447#endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007448 || HL_TABLE()[from_id - 1].sg_cleared)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007449 {
7450 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007451 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7452 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007453#ifdef FEAT_EVAL
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02007454 HL_TABLE()[from_id - 1].sg_script_ctx = current_sctx;
7455 HL_TABLE()[from_id - 1].sg_script_ctx.sc_lnum += sourcing_lnum;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007456#endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007457 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007458 redraw_all_later(SOME_VALID);
Bram Moolenaar99433292017-09-08 12:37:47 +02007459
7460 /* Only call highlight_changed() once after multiple changes. */
7461 need_highlight_changed = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007462 }
7463 }
7464
Bram Moolenaar071d4272004-06-13 20:20:40 +00007465 return;
7466 }
7467
7468 if (doclear)
7469 {
7470 /*
7471 * ":highlight clear [group]" command.
7472 */
7473 line = linep;
7474 if (ends_excmd(*line))
7475 {
7476#ifdef FEAT_GUI
7477 /* First, we do not destroy the old values, but allocate the new
7478 * ones and update the display. THEN we destroy the old values.
7479 * If we destroy the old values first, then the old values
7480 * (such as GuiFont's or GuiFontset's) will still be displayed but
7481 * invalid because they were free'd.
7482 */
7483 if (gui.in_use)
7484 {
7485# ifdef FEAT_BEVAL_TIP
7486 gui_init_tooltip_font();
7487# endif
7488# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7489 gui_init_menu_font();
7490# endif
7491 }
7492# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7493 gui_mch_def_colors();
7494# endif
7495# ifdef FEAT_GUI_X11
7496# ifdef FEAT_MENU
7497
7498 /* This only needs to be done when there is no Menu highlight
7499 * group defined by default, which IS currently the case.
7500 */
7501 gui_mch_new_menu_colors();
7502# endif
7503 if (gui.in_use)
7504 {
7505 gui_new_scrollbar_colors();
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01007506# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007507 gui_mch_new_tooltip_colors();
7508# endif
7509# ifdef FEAT_MENU
7510 gui_mch_new_menu_font();
7511# endif
7512 }
7513# endif
7514
7515 /* Ok, we're done allocating the new default graphics items.
7516 * The screen should already be refreshed at this point.
7517 * It is now Ok to clear out the old data.
7518 */
7519#endif
7520#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007521 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007522#endif
7523 restore_cterm_colors();
7524
7525 /*
7526 * Clear all default highlight groups and load the defaults.
7527 */
7528 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7529 highlight_clear(idx);
7530 init_highlight(TRUE, TRUE);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007531#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007532 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007533 highlight_gui_started();
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007534 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007535#endif
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007536 highlight_changed();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007537 redraw_later_clear();
7538 return;
7539 }
7540 name_end = skiptowhite(line);
7541 linep = skipwhite(name_end);
7542 }
7543
7544 /*
7545 * Find the group name in the table. If it does not exist yet, add it.
7546 */
7547 id = syn_check_group(line, (int)(name_end - line));
7548 if (id == 0) /* failed (out of memory) */
7549 return;
7550 idx = id - 1; /* index is ID minus one */
7551
7552 /* Return if "default" was used and the group already has settings. */
7553 if (dodefault && hl_has_settings(idx, TRUE))
7554 return;
7555
Bram Moolenaar99433292017-09-08 12:37:47 +02007556 /* Make a copy so we can check if any attribute actually changed. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007557 item_before = HL_TABLE()[idx];
Bram Moolenaar99433292017-09-08 12:37:47 +02007558
Bram Moolenaar414168d2017-09-10 15:21:55 +02007559 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007560 is_normal_group = TRUE;
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01007561#ifdef FEAT_TERMINAL
7562 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TERMINAL") == 0)
7563 is_terminal_group = TRUE;
7564#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007565#ifdef FEAT_GUI_X11
Bram Moolenaar414168d2017-09-10 15:21:55 +02007566 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007567 is_menu_group = TRUE;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007568 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007569 is_scrollbar_group = TRUE;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007570 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007571 is_tooltip_group = TRUE;
7572#endif
7573
7574 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7575 if (doclear || (forceit && init))
7576 {
7577 highlight_clear(idx);
7578 if (!doclear)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007579 HL_TABLE()[idx].sg_set = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007580 }
7581
7582 if (!doclear)
7583 while (!ends_excmd(*linep))
7584 {
7585 key_start = linep;
7586 if (*linep == '=')
7587 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007588 semsg(_("E415: unexpected equal sign: %s"), key_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007589 error = TRUE;
7590 break;
7591 }
7592
7593 /*
7594 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7595 * "guibg").
7596 */
Bram Moolenaar1c465442017-03-12 20:10:05 +01007597 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00007598 ++linep;
7599 vim_free(key);
7600 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7601 if (key == NULL)
7602 {
7603 error = TRUE;
7604 break;
7605 }
7606 linep = skipwhite(linep);
7607
7608 if (STRCMP(key, "NONE") == 0)
7609 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007610 if (!init || HL_TABLE()[idx].sg_set == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007611 {
7612 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007613 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007614 highlight_clear(idx);
7615 }
7616 continue;
7617 }
7618
7619 /*
7620 * Check for the equal sign.
7621 */
7622 if (*linep != '=')
7623 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007624 semsg(_("E416: missing equal sign: %s"), key_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007625 error = TRUE;
7626 break;
7627 }
7628 ++linep;
7629
7630 /*
7631 * Isolate the argument.
7632 */
7633 linep = skipwhite(linep);
7634 if (*linep == '\'') /* guifg='color name' */
7635 {
7636 arg_start = ++linep;
7637 linep = vim_strchr(linep, '\'');
7638 if (linep == NULL)
7639 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007640 semsg(_(e_invarg2), key_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007641 error = TRUE;
7642 break;
7643 }
7644 }
7645 else
7646 {
7647 arg_start = linep;
7648 linep = skiptowhite(linep);
7649 }
7650 if (linep == arg_start)
7651 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007652 semsg(_("E417: missing argument: %s"), key_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007653 error = TRUE;
7654 break;
7655 }
7656 vim_free(arg);
7657 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7658 if (arg == NULL)
7659 {
7660 error = TRUE;
7661 break;
7662 }
7663 if (*linep == '\'')
7664 ++linep;
7665
7666 /*
7667 * Store the argument.
7668 */
7669 if ( STRCMP(key, "TERM") == 0
7670 || STRCMP(key, "CTERM") == 0
7671 || STRCMP(key, "GUI") == 0)
7672 {
7673 attr = 0;
7674 off = 0;
7675 while (arg[off] != NUL)
7676 {
7677 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7678 {
7679 len = (int)STRLEN(hl_name_table[i]);
7680 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7681 {
7682 attr |= hl_attr_table[i];
7683 off += len;
7684 break;
7685 }
7686 }
7687 if (i < 0)
7688 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007689 semsg(_("E418: Illegal value: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007690 error = TRUE;
7691 break;
7692 }
7693 if (arg[off] == ',') /* another one follows */
7694 ++off;
7695 }
7696 if (error)
7697 break;
7698 if (*key == 'T')
7699 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007700 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007701 {
7702 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007703 HL_TABLE()[idx].sg_set |= SG_TERM;
7704 HL_TABLE()[idx].sg_term = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007705 }
7706 }
7707 else if (*key == 'C')
7708 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007709 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007710 {
7711 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007712 HL_TABLE()[idx].sg_set |= SG_CTERM;
7713 HL_TABLE()[idx].sg_cterm = attr;
7714 HL_TABLE()[idx].sg_cterm_bold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007715 }
7716 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007717#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007718 else
7719 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007720 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007721 {
7722 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007723 HL_TABLE()[idx].sg_set |= SG_GUI;
7724 HL_TABLE()[idx].sg_gui = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007725 }
7726 }
7727#endif
7728 }
7729 else if (STRCMP(key, "FONT") == 0)
7730 {
7731 /* in non-GUI fonts are simply ignored */
7732#ifdef FEAT_GUI
Bram Moolenaar414168d2017-09-10 15:21:55 +02007733 if (HL_TABLE()[idx].sg_font_name != NULL
7734 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
Bram Moolenaar99433292017-09-08 12:37:47 +02007735 {
7736 /* Font name didn't change, ignore. */
7737 }
7738 else if (!gui.shell_created)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007739 {
7740 /* GUI not started yet, always accept the name. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007741 vim_free(HL_TABLE()[idx].sg_font_name);
7742 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007743 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007744 }
7745 else
7746 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007747 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007748# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007749 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007750# endif
7751 /* First, save the current font/fontset.
7752 * Then try to allocate the font/fontset.
Bram Moolenaar414168d2017-09-10 15:21:55 +02007753 * If the allocation fails, HL_TABLE()[idx].sg_font OR
Bram Moolenaar071d4272004-06-13 20:20:40 +00007754 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7755 */
7756
Bram Moolenaar414168d2017-09-10 15:21:55 +02007757 HL_TABLE()[idx].sg_font = NOFONT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007758# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007759 HL_TABLE()[idx].sg_fontset = NOFONTSET;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007760# endif
7761 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007762 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007763
7764# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007765 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007766 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007767 /* New fontset was accepted. Free the old one, if there
7768 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007769 gui_mch_free_fontset(temp_sg_fontset);
Bram Moolenaar414168d2017-09-10 15:21:55 +02007770 vim_free(HL_TABLE()[idx].sg_font_name);
7771 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007772 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007773 }
7774 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007775 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007776# endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007777 if (HL_TABLE()[idx].sg_font != NOFONT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007778 {
7779 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007780 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007781 gui_mch_free_font(temp_sg_font);
Bram Moolenaar414168d2017-09-10 15:21:55 +02007782 vim_free(HL_TABLE()[idx].sg_font_name);
7783 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007784 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007785 }
7786 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007787 HL_TABLE()[idx].sg_font = temp_sg_font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007788 }
7789#endif
7790 }
7791 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7792 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007793 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007794 {
7795 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007796 HL_TABLE()[idx].sg_set |= SG_CTERM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007797
7798 /* When setting the foreground color, and previously the "bold"
7799 * flag was set for a light color, reset it now */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007800 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007801 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007802 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7803 HL_TABLE()[idx].sg_cterm_bold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007804 }
7805
7806 if (VIM_ISDIGIT(*arg))
7807 color = atoi((char *)arg);
7808 else if (STRICMP(arg, "fg") == 0)
7809 {
7810 if (cterm_normal_fg_color)
7811 color = cterm_normal_fg_color - 1;
7812 else
7813 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007814 emsg(_("E419: FG color unknown"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007815 error = TRUE;
7816 break;
7817 }
7818 }
7819 else if (STRICMP(arg, "bg") == 0)
7820 {
7821 if (cterm_normal_bg_color > 0)
7822 color = cterm_normal_bg_color - 1;
7823 else
7824 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007825 emsg(_("E420: BG color unknown"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007826 error = TRUE;
7827 break;
7828 }
7829 }
7830 else
7831 {
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007832 int bold = MAYBE;
7833
Bram Moolenaar071d4272004-06-13 20:20:40 +00007834#if defined(__QNXNTO__)
7835 static int *color_numbers_8_qansi = color_numbers_8;
7836 /* On qnx, the 8 & 16 color arrays are the same */
7837 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7838 color_numbers_8_qansi = color_numbers_16;
7839#endif
7840
7841 /* reduce calls to STRICMP a bit, it can be slow */
7842 off = TOUPPER_ASC(*arg);
7843 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7844 if (off == color_names[i][0]
7845 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7846 break;
7847 if (i < 0)
7848 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007849 semsg(_("E421: Color name or number not recognized: %s"), key_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007850 error = TRUE;
7851 break;
7852 }
7853
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007854 color = lookup_color(i, key[5] == 'F', &bold);
7855
7856 /* set/reset bold attribute to get light foreground
7857 * colors (on some terminals, e.g. "linux") */
7858 if (bold == TRUE)
7859 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007860 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7861 HL_TABLE()[idx].sg_cterm_bold = TRUE;
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007862 }
7863 else if (bold == FALSE)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007864 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007865 }
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007866
Bram Moolenaarccbab932010-05-13 15:40:30 +02007867 /* Add one to the argument, to avoid zero. Zero is used for
7868 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007869 if (key[5] == 'F')
7870 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007871 HL_TABLE()[idx].sg_cterm_fg = color + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007872 if (is_normal_group)
7873 {
7874 cterm_normal_fg_color = color + 1;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007875 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007876#ifdef FEAT_GUI
7877 /* Don't do this if the GUI is used. */
7878 if (!gui.in_use && !gui.starting)
7879#endif
7880 {
7881 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007882 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007883 term_fg_color(color);
7884 }
7885 }
7886 }
7887 else
7888 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007889 HL_TABLE()[idx].sg_cterm_bg = color + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007890 if (is_normal_group)
7891 {
7892 cterm_normal_bg_color = color + 1;
7893#ifdef FEAT_GUI
7894 /* Don't mess with 'background' if the GUI is used. */
7895 if (!gui.in_use && !gui.starting)
7896#endif
7897 {
7898 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007899 if (color >= 0)
7900 {
Bram Moolenaar1615b362017-06-04 21:06:09 +02007901 int dark = -1;
7902
Bram Moolenaarccbab932010-05-13 15:40:30 +02007903 if (termcap_active)
7904 term_bg_color(color);
7905 if (t_colors < 16)
Bram Moolenaar1615b362017-06-04 21:06:09 +02007906 dark = (color == 0 || color == 4);
7907 /* Limit the heuristic to the standard 16 colors */
7908 else if (color < 16)
7909 dark = (color < 7 || color == 8);
Bram Moolenaarccbab932010-05-13 15:40:30 +02007910 /* Set the 'background' option if the value is
7911 * wrong. */
Bram Moolenaar1615b362017-06-04 21:06:09 +02007912 if (dark != -1
7913 && dark != (*p_bg == 'd')
7914 && !option_was_set((char_u *)"bg"))
7915 {
Bram Moolenaarccbab932010-05-13 15:40:30 +02007916 set_option_value((char_u *)"bg", 0L,
Bram Moolenaar1615b362017-06-04 21:06:09 +02007917 (char_u *)(dark ? "dark" : "light"), 0);
7918 reset_option_was_set((char_u *)"bg");
7919 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007920 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007921 }
7922 }
7923 }
7924 }
7925 }
7926 else if (STRCMP(key, "GUIFG") == 0)
7927 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02007928#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02007929 char_u **namep = &HL_TABLE()[idx].sg_gui_fg_name;
7930
Bram Moolenaar414168d2017-09-10 15:21:55 +02007931 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007932 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007933 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007934 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007935
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007936# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007937 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007938 i = color_name2handle(arg);
Bram Moolenaar63122562016-04-30 12:28:15 +02007939 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007940 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007941 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007942# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02007943 if (*namep == NULL || STRCMP(*namep, arg) != 0)
7944 {
7945 vim_free(*namep);
7946 if (STRCMP(arg, "NONE") != 0)
7947 *namep = vim_strsave(arg);
7948 else
7949 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007950 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02007951 }
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007952# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007953# ifdef FEAT_GUI_X11
Bram Moolenaar452030e2017-09-25 22:57:27 +02007954 if (is_menu_group && gui.menu_fg_pixel != i)
7955 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007956 gui.menu_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02007957 do_colors = TRUE;
7958 }
7959 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
7960 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007961 gui.scroll_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02007962 do_colors = TRUE;
7963 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01007964# ifdef FEAT_BEVAL_GUI
Bram Moolenaar452030e2017-09-25 22:57:27 +02007965 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
7966 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007967 gui.tooltip_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02007968 do_colors = TRUE;
7969 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007970# endif
Bram Moolenaar61623362010-07-14 22:04:22 +02007971# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007972 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007973# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007974 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007975#endif
7976 }
7977 else if (STRCMP(key, "GUIBG") == 0)
7978 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02007979#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02007980 char_u **namep = &HL_TABLE()[idx].sg_gui_bg_name;
7981
Bram Moolenaar414168d2017-09-10 15:21:55 +02007982 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007983 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007984 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007985 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007986
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007987# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007988 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007989 i = color_name2handle(arg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007990 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007991 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007992 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007993# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02007994 if (*namep == NULL || STRCMP(*namep, arg) != 0)
7995 {
7996 vim_free(*namep);
7997 if (STRCMP(arg, "NONE") != 0)
7998 *namep = vim_strsave(arg);
7999 else
8000 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008001 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008002 }
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008003# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008004# ifdef FEAT_GUI_X11
Bram Moolenaar452030e2017-09-25 22:57:27 +02008005 if (is_menu_group && gui.menu_bg_pixel != i)
8006 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008007 gui.menu_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008008 do_colors = TRUE;
8009 }
8010 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
8011 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008012 gui.scroll_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008013 do_colors = TRUE;
8014 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008015# ifdef FEAT_BEVAL_GUI
Bram Moolenaar452030e2017-09-25 22:57:27 +02008016 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
8017 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008018 gui.tooltip_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008019 do_colors = TRUE;
8020 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008021# endif
Bram Moolenaar61623362010-07-14 22:04:22 +02008022# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008023 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008024# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008025 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008026#endif
8027 }
8028 else if (STRCMP(key, "GUISP") == 0)
8029 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02008030#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02008031 char_u **namep = &HL_TABLE()[idx].sg_gui_sp_name;
8032
Bram Moolenaar414168d2017-09-10 15:21:55 +02008033 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008034 {
8035 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008036 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008037
Bram Moolenaar61623362010-07-14 22:04:22 +02008038# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008039 i = color_name2handle(arg);
8040 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
8041 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008042 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008043# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02008044 if (*namep == NULL || STRCMP(*namep, arg) != 0)
8045 {
8046 vim_free(*namep);
8047 if (STRCMP(arg, "NONE") != 0)
8048 *namep = vim_strsave(arg);
8049 else
8050 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008051 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008052 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008053# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008054 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008055# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008056 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008057#endif
8058 }
8059 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
8060 {
8061 char_u buf[100];
8062 char_u *tname;
8063
8064 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008065 HL_TABLE()[idx].sg_set |= SG_TERM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008066
8067 /*
8068 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008069 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008070 */
8071 if (STRNCMP(arg, "t_", 2) == 0)
8072 {
8073 off = 0;
8074 buf[0] = 0;
8075 while (arg[off] != NUL)
8076 {
8077 /* Isolate one termcap name */
8078 for (len = 0; arg[off + len] &&
8079 arg[off + len] != ','; ++len)
8080 ;
8081 tname = vim_strnsave(arg + off, len);
8082 if (tname == NULL) /* out of memory */
8083 {
8084 error = TRUE;
8085 break;
8086 }
8087 /* lookup the escape sequence for the item */
8088 p = get_term_code(tname);
8089 vim_free(tname);
8090 if (p == NULL) /* ignore non-existing things */
8091 p = (char_u *)"";
8092
8093 /* Append it to the already found stuff */
8094 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
8095 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01008096 semsg(_("E422: terminal code too long: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008097 error = TRUE;
8098 break;
8099 }
8100 STRCAT(buf, p);
8101
8102 /* Advance to the next item */
8103 off += len;
8104 if (arg[off] == ',') /* another one follows */
8105 ++off;
8106 }
8107 }
8108 else
8109 {
8110 /*
8111 * Copy characters from arg[] to buf[], translating <> codes.
8112 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008113 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00008114 {
Bram Moolenaar35a4cfa2016-08-14 16:07:48 +02008115 len = trans_special(&p, buf + off, FALSE, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008116 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008117 off += len;
8118 else /* copy as normal char */
8119 buf[off++] = *p++;
8120 }
8121 buf[off] = NUL;
8122 }
8123 if (error)
8124 break;
8125
8126 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
8127 p = NULL;
8128 else
8129 p = vim_strsave(buf);
8130 if (key[2] == 'A')
8131 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008132 vim_free(HL_TABLE()[idx].sg_start);
8133 HL_TABLE()[idx].sg_start = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008134 }
8135 else
8136 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008137 vim_free(HL_TABLE()[idx].sg_stop);
8138 HL_TABLE()[idx].sg_stop = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008139 }
8140 }
8141 else
8142 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01008143 semsg(_("E423: Illegal argument: %s"), key_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008144 error = TRUE;
8145 break;
8146 }
Bram Moolenaar414168d2017-09-10 15:21:55 +02008147 HL_TABLE()[idx].sg_cleared = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008148
8149 /*
8150 * When highlighting has been given for a group, don't link it.
8151 */
Bram Moolenaar414168d2017-09-10 15:21:55 +02008152 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8153 HL_TABLE()[idx].sg_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008154
8155 /*
8156 * Continue with next argument.
8157 */
8158 linep = skipwhite(linep);
8159 }
8160
8161 /*
8162 * If there is an error, and it's a new entry, remove it from the table.
8163 */
8164 if (error && idx == highlight_ga.ga_len)
8165 syn_unadd_group();
8166 else
8167 {
8168 if (is_normal_group)
8169 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008170 HL_TABLE()[idx].sg_term_attr = 0;
8171 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008172#ifdef FEAT_GUI
Bram Moolenaar414168d2017-09-10 15:21:55 +02008173 HL_TABLE()[idx].sg_gui_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008174 /*
8175 * Need to update all groups, because they might be using "bg"
8176 * and/or "fg", which have been changed now.
8177 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008178#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008179#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008180 if (USE_24BIT)
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008181 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008182 highlight_gui_started();
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008183 did_highlight_changed = TRUE;
8184 redraw_all_later(NOT_VALID);
8185 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008186#endif
8187 }
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01008188#ifdef FEAT_TERMINAL
8189 else if (is_terminal_group)
8190 set_terminal_default_colors(
8191 HL_TABLE()[idx].sg_cterm_fg, HL_TABLE()[idx].sg_cterm_bg);
8192#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008193#ifdef FEAT_GUI_X11
8194# ifdef FEAT_MENU
8195 else if (is_menu_group)
8196 {
8197 if (gui.in_use && do_colors)
8198 gui_mch_new_menu_colors();
8199 }
8200# endif
8201 else if (is_scrollbar_group)
8202 {
8203 if (gui.in_use && do_colors)
8204 gui_new_scrollbar_colors();
8205 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008206# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00008207 else if (is_tooltip_group)
8208 {
8209 if (gui.in_use && do_colors)
8210 gui_mch_new_tooltip_colors();
8211 }
8212# endif
8213#endif
8214 else
8215 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008216#ifdef FEAT_EVAL
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02008217 HL_TABLE()[idx].sg_script_ctx = current_sctx;
8218 HL_TABLE()[idx].sg_script_ctx.sc_lnum += sourcing_lnum;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008219#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008220 }
Bram Moolenaar99433292017-09-08 12:37:47 +02008221
Bram Moolenaar071d4272004-06-13 20:20:40 +00008222 vim_free(key);
8223 vim_free(arg);
8224
Bram Moolenaar99433292017-09-08 12:37:47 +02008225 /* Only call highlight_changed() once, after a sequence of highlight
8226 * commands, and only if an attribute actually changed. */
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008227 if ((did_change
8228 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008229#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
8230 && !did_highlight_changed
8231#endif
8232 )
Bram Moolenaar99433292017-09-08 12:37:47 +02008233 {
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008234 /* Do not trigger a redraw when highlighting is changed while
8235 * redrawing. This may happen when evaluating 'statusline' changes the
8236 * StatusLine group. */
8237 if (!updating_screen)
8238 redraw_all_later(NOT_VALID);
Bram Moolenaar99433292017-09-08 12:37:47 +02008239 need_highlight_changed = TRUE;
8240 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008241}
8242
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008243#if defined(EXITFREE) || defined(PROTO)
8244 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008245free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008246{
8247 int i;
8248
8249 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008250 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008251 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008252 vim_free(HL_TABLE()[i].sg_name);
8253 vim_free(HL_TABLE()[i].sg_name_u);
8254 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008255 ga_clear(&highlight_ga);
8256}
8257#endif
8258
Bram Moolenaar071d4272004-06-13 20:20:40 +00008259/*
8260 * Reset the cterm colors to what they were before Vim was started, if
8261 * possible. Otherwise reset them to zero.
8262 */
8263 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008264restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008265{
Bram Moolenaar4f974752019-02-17 17:44:42 +01008266#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008267 /* Since t_me has been set, this probably means that the user
8268 * wants to use this as default colors. Need to reset default
8269 * background/foreground colors. */
8270 mch_set_normal_colors();
8271#else
Bram Moolenaarafde13b2019-04-28 19:46:49 +02008272# ifdef VIMDLL
8273 if (!gui.in_use)
8274 {
8275 mch_set_normal_colors();
8276 return;
8277 }
8278# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008279 cterm_normal_fg_color = 0;
8280 cterm_normal_fg_bold = 0;
8281 cterm_normal_bg_color = 0;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008282# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008283 cterm_normal_fg_gui_color = INVALCOLOR;
8284 cterm_normal_bg_gui_color = INVALCOLOR;
8285# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008286#endif
8287}
8288
8289/*
8290 * Return TRUE if highlight group "idx" has any settings.
8291 * When "check_link" is TRUE also check for an existing link.
8292 */
8293 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008294hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008295{
8296 return ( HL_TABLE()[idx].sg_term_attr != 0
8297 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008298 || HL_TABLE()[idx].sg_cterm_fg != 0
8299 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008300#ifdef FEAT_GUI
8301 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008302 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8303 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8304 || HL_TABLE()[idx].sg_gui_sp_name != NULL
Bram Moolenaar87748452017-03-12 17:10:33 +01008305 || HL_TABLE()[idx].sg_font_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008306#endif
8307 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8308}
8309
8310/*
8311 * Clear highlighting for one group.
8312 */
8313 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008314highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008315{
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008316 HL_TABLE()[idx].sg_cleared = TRUE;
8317
Bram Moolenaar071d4272004-06-13 20:20:40 +00008318 HL_TABLE()[idx].sg_term = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +01008319 VIM_CLEAR(HL_TABLE()[idx].sg_start);
8320 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008321 HL_TABLE()[idx].sg_term_attr = 0;
8322 HL_TABLE()[idx].sg_cterm = 0;
8323 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8324 HL_TABLE()[idx].sg_cterm_fg = 0;
8325 HL_TABLE()[idx].sg_cterm_bg = 0;
8326 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008327#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008328 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +01008329 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
8330 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
8331 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
Bram Moolenaar61623362010-07-14 22:04:22 +02008332#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008333#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008334 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8335 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008336#endif
8337#ifdef FEAT_GUI
Bram Moolenaar61623362010-07-14 22:04:22 +02008338 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008339 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8340 HL_TABLE()[idx].sg_font = NOFONT;
8341# ifdef FEAT_XFONTSET
8342 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8343 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8344# endif
Bram Moolenaard23a8232018-02-10 18:45:26 +01008345 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008346 HL_TABLE()[idx].sg_gui_attr = 0;
8347#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008348#ifdef FEAT_EVAL
8349 /* Clear the script ID only when there is no link, since that is not
8350 * cleared. */
8351 if (HL_TABLE()[idx].sg_link == 0)
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02008352 {
8353 HL_TABLE()[idx].sg_script_ctx.sc_sid = 0;
8354 HL_TABLE()[idx].sg_script_ctx.sc_lnum = 0;
8355 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008356#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008357}
8358
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008359#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008360/*
8361 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008362 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008363 * "Tooltip" colors.
8364 */
8365 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008366set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008367{
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008368# ifdef FEAT_GUI
8369# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008370 if (gui.in_use)
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008371# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008372 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008373 if (set_group_colors((char_u *)"Normal",
8374 &gui.norm_pixel, &gui.back_pixel,
8375 FALSE, TRUE, FALSE))
8376 {
8377 gui_mch_new_colors();
8378 must_redraw = CLEAR;
8379 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008380# ifdef FEAT_GUI_X11
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008381 if (set_group_colors((char_u *)"Menu",
8382 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8383 TRUE, FALSE, FALSE))
8384 {
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008385# ifdef FEAT_MENU
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008386 gui_mch_new_menu_colors();
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008387# endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008388 must_redraw = CLEAR;
8389 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008390# ifdef FEAT_BEVAL_GUI
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008391 if (set_group_colors((char_u *)"Tooltip",
8392 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8393 FALSE, FALSE, TRUE))
8394 {
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008395# ifdef FEAT_TOOLBAR
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008396 gui_mch_new_tooltip_colors();
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008397# endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008398 must_redraw = CLEAR;
8399 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008400# endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008401 if (set_group_colors((char_u *)"Scrollbar",
8402 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8403 FALSE, FALSE, FALSE))
8404 {
8405 gui_new_scrollbar_colors();
8406 must_redraw = CLEAR;
8407 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008408# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008409 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008410# endif
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008411# ifdef FEAT_TERMGUICOLORS
8412# ifdef FEAT_GUI
8413 else
8414# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008415 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008416 int idx;
8417
8418 idx = syn_name2id((char_u *)"Normal") - 1;
8419 if (idx >= 0)
8420 {
8421 gui_do_one_color(idx, FALSE, FALSE);
8422
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008423 /* If the normal fg or bg color changed a complete redraw is
8424 * required. */
8425 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
8426 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008427 {
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008428 /* if the GUI color is INVALCOLOR then we use the default cterm
8429 * color */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008430 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008431 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
8432 must_redraw = CLEAR;
8433 }
8434 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008435 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008436# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008437}
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008438#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008439
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008440#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008441/*
8442 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8443 */
8444 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008445set_group_colors(
8446 char_u *name,
8447 guicolor_T *fgp,
8448 guicolor_T *bgp,
8449 int do_menu,
8450 int use_norm,
8451 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008452{
8453 int idx;
8454
8455 idx = syn_name2id(name) - 1;
8456 if (idx >= 0)
8457 {
8458 gui_do_one_color(idx, do_menu, do_tooltip);
8459
8460 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8461 *fgp = HL_TABLE()[idx].sg_gui_fg;
8462 else if (use_norm)
8463 *fgp = gui.def_norm_pixel;
8464 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8465 *bgp = HL_TABLE()[idx].sg_gui_bg;
8466 else if (use_norm)
8467 *bgp = gui.def_back_pixel;
8468 return TRUE;
8469 }
8470 return FALSE;
8471}
8472
8473/*
8474 * Get the font of the "Normal" group.
8475 * Returns "" when it's not found or not set.
8476 */
8477 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008478hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008479{
8480 int id;
8481 char_u *s;
8482
8483 id = syn_name2id((char_u *)"Normal");
8484 if (id > 0)
8485 {
8486 s = HL_TABLE()[id - 1].sg_font_name;
8487 if (s != NULL)
8488 return s;
8489 }
8490 return (char_u *)"";
8491}
8492
8493/*
8494 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8495 * actually chosen to be used.
8496 */
8497 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008498hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008499{
8500 int id;
8501
8502 id = syn_name2id((char_u *)"Normal");
8503 if (id > 0)
8504 {
8505 vim_free(HL_TABLE()[id - 1].sg_font_name);
8506 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8507 }
8508}
8509
8510/*
8511 * Set background color for "Normal" group. Called by gui_set_bg_color()
8512 * when the color is known.
8513 */
8514 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008515hl_set_bg_color_name(
8516 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008517{
8518 int id;
8519
8520 if (name != NULL)
8521 {
8522 id = syn_name2id((char_u *)"Normal");
8523 if (id > 0)
8524 {
8525 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8526 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8527 }
8528 }
8529}
8530
8531/*
8532 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8533 * when the color is known.
8534 */
8535 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008536hl_set_fg_color_name(
8537 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008538{
8539 int id;
8540
8541 if (name != NULL)
8542 {
8543 id = syn_name2id((char_u *)"Normal");
8544 if (id > 0)
8545 {
8546 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8547 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8548 }
8549 }
8550}
8551
8552/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008553 * Return the handle for a font name.
8554 * Returns NOFONT when failed.
8555 */
8556 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008557font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008558{
8559 if (STRCMP(name, "NONE") == 0)
8560 return NOFONT;
8561
8562 return gui_mch_get_font(name, TRUE);
8563}
8564
8565# ifdef FEAT_XFONTSET
8566/*
8567 * Return the handle for a fontset name.
8568 * Returns NOFONTSET when failed.
8569 */
8570 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008571fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008572{
8573 if (STRCMP(name, "NONE") == 0)
8574 return NOFONTSET;
8575
8576 return gui_mch_get_fontset(name, TRUE, fixed_width);
8577}
8578# endif
8579
8580/*
8581 * Get the font or fontset for one highlight group.
8582 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008583 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008584hl_do_font(
8585 int idx,
8586 char_u *arg,
8587 int do_normal, /* set normal font */
8588 int do_menu UNUSED, /* set menu font */
8589 int do_tooltip UNUSED, /* set tooltip font */
8590 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008591{
8592# ifdef FEAT_XFONTSET
8593 /* If 'guifontset' is not empty, first try using the name as a
8594 * fontset. If that doesn't work, use it as a font name. */
8595 if (*p_guifontset != NUL
8596# ifdef FONTSET_ALWAYS
8597 || do_menu
8598# endif
8599# ifdef FEAT_BEVAL_TIP
8600 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8601 || do_tooltip
8602# endif
8603 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008604 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008605 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008606 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008607 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8608# ifdef FONTSET_ALWAYS
8609 || do_menu
8610# endif
8611# ifdef FEAT_BEVAL_TIP
8612 || do_tooltip
8613# endif
8614 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008615 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008616 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8617 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008618 /* If it worked and it's the Normal group, use it as the normal
8619 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008620 if (do_normal)
8621 gui_init_font(arg, TRUE);
8622# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8623 if (do_menu)
8624 {
8625# ifdef FONTSET_ALWAYS
8626 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8627# else
8628 /* YIKES! This is a bug waiting to crash the program */
8629 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8630# endif
8631 gui_mch_new_menu_font();
8632 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008633# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00008634 if (do_tooltip)
8635 {
8636 /* The Athena widget set cannot currently handle switching between
8637 * displaying a single font and a fontset.
8638 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008639 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008640 * XFontStruct is used.
8641 */
8642 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8643 gui_mch_new_tooltip_font();
8644 }
8645# endif
8646# endif
8647 }
8648 else
8649# endif
8650 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008651 if (free_font)
8652 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008653 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8654 /* If it worked and it's the Normal group, use it as the
8655 * normal font. Same for the Menu group. */
8656 if (HL_TABLE()[idx].sg_font != NOFONT)
8657 {
8658 if (do_normal)
8659 gui_init_font(arg, FALSE);
8660#ifndef FONTSET_ALWAYS
8661# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8662 if (do_menu)
8663 {
8664 gui.menu_font = HL_TABLE()[idx].sg_font;
8665 gui_mch_new_menu_font();
8666 }
8667# endif
8668#endif
8669 }
8670 }
8671}
8672
8673#endif /* FEAT_GUI */
8674
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008675#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008676/*
8677 * Return the handle for a color name.
8678 * Returns INVALCOLOR when failed.
8679 */
Bram Moolenaar3d9bdfe2017-08-12 22:55:58 +02008680 guicolor_T
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008681color_name2handle(char_u *name)
8682{
8683 if (STRCMP(name, "NONE") == 0)
8684 return INVALCOLOR;
8685
8686 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8687 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008688#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008689 if (gui.in_use)
8690#endif
8691#ifdef FEAT_GUI
8692 return gui.norm_pixel;
8693#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008694#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008695 if (cterm_normal_fg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008696 return cterm_normal_fg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008697 /* Guess that the foreground is black or white. */
8698 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008699#endif
8700 }
8701 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8702 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008703#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008704 if (gui.in_use)
8705#endif
8706#ifdef FEAT_GUI
8707 return gui.back_pixel;
8708#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008709#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008710 if (cterm_normal_bg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008711 return cterm_normal_bg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008712 /* Guess that the background is white or black. */
8713 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008714#endif
8715 }
8716
8717 return GUI_GET_COLOR(name);
8718}
8719#endif
8720
Bram Moolenaar071d4272004-06-13 20:20:40 +00008721/*
8722 * Table with the specifications for an attribute number.
8723 * Note that this table is used by ALL buffers. This is required because the
8724 * GUI can redraw at any time for any buffer.
8725 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008726static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008727
8728#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8729
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008730static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008731
8732#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8733
8734#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008735static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008736
8737#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8738#endif
8739
8740/*
8741 * Return the attr number for a set of colors and font.
8742 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8743 * if the combination is new.
8744 * Return 0 for error (no more room).
8745 */
8746 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008747get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008748{
8749 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008750 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008751 static int recursive = FALSE;
8752
8753 /*
8754 * Init the table, in case it wasn't done yet.
8755 */
8756 table->ga_itemsize = sizeof(attrentry_T);
8757 table->ga_growsize = 7;
8758
8759 /*
8760 * Try to find an entry with the same specifications.
8761 */
8762 for (i = 0; i < table->ga_len; ++i)
8763 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008764 taep = &(((attrentry_T *)table->ga_data)[i]);
8765 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008766 && (
8767#ifdef FEAT_GUI
8768 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008769 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8770 && aep->ae_u.gui.bg_color
8771 == taep->ae_u.gui.bg_color
8772 && aep->ae_u.gui.sp_color
8773 == taep->ae_u.gui.sp_color
8774 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008775# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008776 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008777# endif
8778 ))
8779 ||
8780#endif
8781 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008782 && (aep->ae_u.term.start == NULL)
8783 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008784 && (aep->ae_u.term.start == NULL
8785 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008786 taep->ae_u.term.start) == 0)
8787 && (aep->ae_u.term.stop == NULL)
8788 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008789 && (aep->ae_u.term.stop == NULL
8790 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008791 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008792 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008793 && aep->ae_u.cterm.fg_color
8794 == taep->ae_u.cterm.fg_color
8795 && aep->ae_u.cterm.bg_color
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008796 == taep->ae_u.cterm.bg_color
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008797#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008798 && aep->ae_u.cterm.fg_rgb
8799 == taep->ae_u.cterm.fg_rgb
8800 && aep->ae_u.cterm.bg_rgb
8801 == taep->ae_u.cterm.bg_rgb
8802#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008803 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008804
8805 return i + ATTR_OFF;
8806 }
8807
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008808 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008809 {
8810 /*
8811 * Running out of attribute entries! remove all attributes, and
8812 * compute new ones for all groups.
8813 * When called recursively, we are really out of numbers.
8814 */
8815 if (recursive)
8816 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01008817 emsg(_("E424: Too many different highlighting attributes in use"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008818 return 0;
8819 }
8820 recursive = TRUE;
8821
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008822 clear_hl_tables();
8823
Bram Moolenaar071d4272004-06-13 20:20:40 +00008824 must_redraw = CLEAR;
8825
8826 for (i = 0; i < highlight_ga.ga_len; ++i)
8827 set_hl_attr(i);
8828
8829 recursive = FALSE;
8830 }
8831
8832 /*
8833 * This is a new combination of colors and font, add an entry.
8834 */
8835 if (ga_grow(table, 1) == FAIL)
8836 return 0;
8837
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008838 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8839 vim_memset(taep, 0, sizeof(attrentry_T));
8840 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008841#ifdef FEAT_GUI
8842 if (table == &gui_attr_table)
8843 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008844 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8845 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8846 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8847 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008848# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008849 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008850# endif
8851 }
8852#endif
8853 if (table == &term_attr_table)
8854 {
8855 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008856 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008857 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008858 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008859 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008860 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008861 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008862 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008863 }
8864 else if (table == &cterm_attr_table)
8865 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008866 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8867 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008868#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008869 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
8870 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
8871#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008872 }
8873 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008874 return (table->ga_len - 1 + ATTR_OFF);
8875}
8876
Bram Moolenaar113e1072019-01-20 15:30:40 +01008877#if defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008878/*
Bram Moolenaareeac6772017-07-23 15:48:37 +02008879 * Get an attribute index for a cterm entry.
8880 * Uses an existing entry when possible or adds one when needed.
8881 */
8882 int
8883get_cterm_attr_idx(int attr, int fg, int bg)
8884{
8885 attrentry_T at_en;
8886
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008887 vim_memset(&at_en, 0, sizeof(attrentry_T));
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008888#ifdef FEAT_TERMGUICOLORS
8889 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
8890 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
8891#endif
Bram Moolenaareeac6772017-07-23 15:48:37 +02008892 at_en.ae_attr = attr;
8893 at_en.ae_u.cterm.fg_color = fg;
8894 at_en.ae_u.cterm.bg_color = bg;
8895 return get_attr_entry(&cterm_attr_table, &at_en);
8896}
Bram Moolenaar113e1072019-01-20 15:30:40 +01008897#endif
Bram Moolenaareeac6772017-07-23 15:48:37 +02008898
Bram Moolenaar113e1072019-01-20 15:30:40 +01008899#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
Bram Moolenaar065f41c2017-07-23 18:07:56 +02008900/*
8901 * Get an attribute index for a 'termguicolors' entry.
8902 * Uses an existing entry when possible or adds one when needed.
8903 */
8904 int
8905get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8906{
8907 attrentry_T at_en;
8908
8909 vim_memset(&at_en, 0, sizeof(attrentry_T));
8910 at_en.ae_attr = attr;
Bram Moolenaard4fc5772018-02-27 14:39:03 +01008911 if (fg == INVALCOLOR && bg == INVALCOLOR)
8912 {
8913 /* If both GUI colors are not set fall back to the cterm colors. Helps
8914 * if the GUI only has an attribute, such as undercurl. */
8915 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
8916 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
8917 }
8918 else
8919 {
8920 at_en.ae_u.cterm.fg_rgb = fg;
8921 at_en.ae_u.cterm.bg_rgb = bg;
8922 }
Bram Moolenaar065f41c2017-07-23 18:07:56 +02008923 return get_attr_entry(&cterm_attr_table, &at_en);
8924}
8925#endif
8926
Bram Moolenaar113e1072019-01-20 15:30:40 +01008927#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008928/*
8929 * Get an attribute index for a cterm entry.
8930 * Uses an existing entry when possible or adds one when needed.
8931 */
8932 int
8933get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8934{
8935 attrentry_T at_en;
8936
8937 vim_memset(&at_en, 0, sizeof(attrentry_T));
8938 at_en.ae_attr = attr;
8939 at_en.ae_u.gui.fg_color = fg;
8940 at_en.ae_u.gui.bg_color = bg;
8941 return get_attr_entry(&gui_attr_table, &at_en);
8942}
8943#endif
8944
Bram Moolenaareeac6772017-07-23 15:48:37 +02008945/*
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008946 * Clear all highlight tables.
8947 */
8948 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008949clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008950{
8951 int i;
8952 attrentry_T *taep;
8953
8954#ifdef FEAT_GUI
8955 ga_clear(&gui_attr_table);
8956#endif
8957 for (i = 0; i < term_attr_table.ga_len; ++i)
8958 {
8959 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8960 vim_free(taep->ae_u.term.start);
8961 vim_free(taep->ae_u.term.stop);
8962 }
8963 ga_clear(&term_attr_table);
8964 ga_clear(&cterm_attr_table);
8965}
8966
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008967#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008968/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008969 * Combine special attributes (e.g., for spelling) with other attributes
8970 * (e.g., for syntax highlighting).
8971 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008972 * This creates a new group when required.
8973 * Since we expect there to be few spelling mistakes we don't cache the
8974 * result.
8975 * Return the resulting attributes.
8976 */
8977 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008978hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008979{
8980 attrentry_T *char_aep = NULL;
8981 attrentry_T *spell_aep;
8982 attrentry_T new_en;
8983
8984 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008985 return prim_attr;
8986 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02008987 return ATTR_COMBINE(char_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008988#ifdef FEAT_GUI
8989 if (gui.in_use)
8990 {
8991 if (char_attr > HL_ALL)
8992 char_aep = syn_gui_attr2entry(char_attr);
8993 if (char_aep != NULL)
8994 new_en = *char_aep;
8995 else
8996 {
8997 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008998 new_en.ae_u.gui.fg_color = INVALCOLOR;
8999 new_en.ae_u.gui.bg_color = INVALCOLOR;
9000 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00009001 if (char_attr <= HL_ALL)
9002 new_en.ae_attr = char_attr;
9003 }
9004
Bram Moolenaar30abd282005-06-22 22:35:10 +00009005 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009006 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009007 else
9008 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00009009 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009010 if (spell_aep != NULL)
9011 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009012 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
9013 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009014 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
9015 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
9016 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
9017 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
9018 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
9019 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
9020 if (spell_aep->ae_u.gui.font != NOFONT)
9021 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
9022# ifdef FEAT_XFONTSET
9023 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
9024 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
9025# endif
9026 }
9027 }
9028 return get_attr_entry(&gui_attr_table, &new_en);
9029 }
9030#endif
9031
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009032 if (IS_CTERM)
Bram Moolenaar217ad922005-03-20 22:37:15 +00009033 {
9034 if (char_attr > HL_ALL)
9035 char_aep = syn_cterm_attr2entry(char_attr);
9036 if (char_aep != NULL)
9037 new_en = *char_aep;
9038 else
9039 {
9040 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaar0cdb72a2017-01-02 21:37:40 +01009041#ifdef FEAT_TERMGUICOLORS
9042 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
9043 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
9044#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00009045 if (char_attr <= HL_ALL)
9046 new_en.ae_attr = char_attr;
9047 }
9048
Bram Moolenaar30abd282005-06-22 22:35:10 +00009049 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009050 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009051 else
9052 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00009053 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009054 if (spell_aep != NULL)
9055 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009056 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
9057 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009058 if (spell_aep->ae_u.cterm.fg_color > 0)
9059 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
9060 if (spell_aep->ae_u.cterm.bg_color > 0)
9061 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009062#ifdef FEAT_TERMGUICOLORS
Bram Moolenaard4fc5772018-02-27 14:39:03 +01009063 /* If both fg and bg are not set fall back to cterm colors.
9064 * Helps for SpellBad which uses undercurl in the GUI. */
9065 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
9066 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
9067 {
9068 if (spell_aep->ae_u.cterm.fg_color > 0)
9069 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
9070 if (spell_aep->ae_u.cterm.bg_color > 0)
9071 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
9072 }
9073 else
9074 {
9075 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
9076 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
9077 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
9078 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
9079 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009080#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00009081 }
9082 }
9083 return get_attr_entry(&cterm_attr_table, &new_en);
9084 }
9085
9086 if (char_attr > HL_ALL)
9087 char_aep = syn_term_attr2entry(char_attr);
9088 if (char_aep != NULL)
9089 new_en = *char_aep;
9090 else
9091 {
9092 vim_memset(&new_en, 0, sizeof(new_en));
9093 if (char_attr <= HL_ALL)
9094 new_en.ae_attr = char_attr;
9095 }
9096
Bram Moolenaar30abd282005-06-22 22:35:10 +00009097 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009098 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009099 else
9100 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00009101 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009102 if (spell_aep != NULL)
9103 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009104 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009105 if (spell_aep->ae_u.term.start != NULL)
9106 {
9107 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
9108 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
9109 }
9110 }
9111 }
9112 return get_attr_entry(&term_attr_table, &new_en);
9113}
9114#endif
9115
Bram Moolenaar071d4272004-06-13 20:20:40 +00009116#ifdef FEAT_GUI
9117
9118 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009119syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009120{
9121 attr -= ATTR_OFF;
9122 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
9123 return NULL;
9124 return &(GUI_ATTR_ENTRY(attr));
9125}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009126#endif /* FEAT_GUI */
9127
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009128/*
9129 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
9130 * Only to be used when "attr" > HL_ALL.
9131 */
9132 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009133syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009134{
9135 attrentry_T *aep;
9136
9137#ifdef FEAT_GUI
9138 if (gui.in_use)
9139 aep = syn_gui_attr2entry(attr);
9140 else
9141#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009142 if (IS_CTERM)
9143 aep = syn_cterm_attr2entry(attr);
9144 else
9145 aep = syn_term_attr2entry(attr);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009146
9147 if (aep == NULL) /* highlighting not set */
9148 return 0;
9149 return aep->ae_attr;
9150}
9151
9152
Bram Moolenaar071d4272004-06-13 20:20:40 +00009153 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009154syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009155{
9156 attr -= ATTR_OFF;
9157 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
9158 return NULL;
9159 return &(TERM_ATTR_ENTRY(attr));
9160}
9161
9162 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009163syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009164{
9165 attr -= ATTR_OFF;
9166 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
9167 return NULL;
9168 return &(CTERM_ATTR_ENTRY(attr));
9169}
9170
9171#define LIST_ATTR 1
9172#define LIST_STRING 2
9173#define LIST_INT 3
9174
9175 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009176highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009177{
9178 struct hl_group *sgp;
9179 int didh = FALSE;
9180
Bram Moolenaarf86db782018-10-25 13:31:37 +02009181 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
9182
9183 if (message_filtered(sgp->sg_name))
9184 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009185
9186 didh = highlight_list_arg(id, didh, LIST_ATTR,
9187 sgp->sg_term, NULL, "term");
9188 didh = highlight_list_arg(id, didh, LIST_STRING,
9189 0, sgp->sg_start, "start");
9190 didh = highlight_list_arg(id, didh, LIST_STRING,
9191 0, sgp->sg_stop, "stop");
9192
9193 didh = highlight_list_arg(id, didh, LIST_ATTR,
9194 sgp->sg_cterm, NULL, "cterm");
9195 didh = highlight_list_arg(id, didh, LIST_INT,
9196 sgp->sg_cterm_fg, NULL, "ctermfg");
9197 didh = highlight_list_arg(id, didh, LIST_INT,
9198 sgp->sg_cterm_bg, NULL, "ctermbg");
9199
Bram Moolenaar61623362010-07-14 22:04:22 +02009200#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009201 didh = highlight_list_arg(id, didh, LIST_ATTR,
9202 sgp->sg_gui, NULL, "gui");
9203 didh = highlight_list_arg(id, didh, LIST_STRING,
9204 0, sgp->sg_gui_fg_name, "guifg");
9205 didh = highlight_list_arg(id, didh, LIST_STRING,
9206 0, sgp->sg_gui_bg_name, "guibg");
9207 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009208 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02009209#endif
9210#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009211 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00009212 0, sgp->sg_font_name, "font");
9213#endif
9214
Bram Moolenaar661b1822005-07-28 22:36:45 +00009215 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009216 {
9217 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00009218 didh = TRUE;
Bram Moolenaar32526b32019-01-19 17:43:09 +01009219 msg_puts_attr("links to", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009220 msg_putchar(' ');
9221 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
9222 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009223
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009224 if (!didh)
9225 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00009226#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009227 if (p_verbose > 0)
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02009228 last_set_msg(sgp->sg_script_ctx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00009229#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009230}
9231
9232 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009233highlight_list_arg(
9234 int id,
9235 int didh,
9236 int type,
9237 int iarg,
9238 char_u *sarg,
9239 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009240{
9241 char_u buf[100];
9242 char_u *ts;
9243 int i;
9244
Bram Moolenaar661b1822005-07-28 22:36:45 +00009245 if (got_int)
9246 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009247 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
9248 {
9249 ts = buf;
9250 if (type == LIST_INT)
9251 sprintf((char *)buf, "%d", iarg - 1);
9252 else if (type == LIST_STRING)
9253 ts = sarg;
9254 else /* type == LIST_ATTR */
9255 {
9256 buf[0] = NUL;
9257 for (i = 0; hl_attr_table[i] != 0; ++i)
9258 {
9259 if (iarg & hl_attr_table[i])
9260 {
9261 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02009262 vim_strcat(buf, (char_u *)",", 100);
9263 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009264 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
9265 }
9266 }
9267 }
9268
9269 (void)syn_list_header(didh,
9270 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9271 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00009272 if (!got_int)
9273 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009274 if (*name != NUL)
9275 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01009276 msg_puts_attr(name, HL_ATTR(HLF_D));
9277 msg_puts_attr("=", HL_ATTR(HLF_D));
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009278 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009279 msg_outtrans(ts);
9280 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009281 }
9282 return didh;
9283}
9284
9285#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9286/*
9287 * Return "1" if highlight group "id" has attribute "flag".
9288 * Return NULL otherwise.
9289 */
9290 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009291highlight_has_attr(
9292 int id,
9293 int flag,
9294 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009295{
9296 int attr;
9297
9298 if (id <= 0 || id > highlight_ga.ga_len)
9299 return NULL;
9300
Bram Moolenaar61623362010-07-14 22:04:22 +02009301#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009302 if (modec == 'g')
9303 attr = HL_TABLE()[id - 1].sg_gui;
9304 else
9305#endif
9306 if (modec == 'c')
9307 attr = HL_TABLE()[id - 1].sg_cterm;
9308 else
9309 attr = HL_TABLE()[id - 1].sg_term;
9310
9311 if (attr & flag)
9312 return (char_u *)"1";
9313 return NULL;
9314}
9315#endif
9316
9317#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9318/*
9319 * Return color name of highlight group "id".
9320 */
9321 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009322highlight_color(
9323 int id,
9324 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
9325 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009326{
9327 static char_u name[20];
9328 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009329 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009330 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009331 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009332
9333 if (id <= 0 || id > highlight_ga.ga_len)
9334 return NULL;
9335
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009336 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009337 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009338 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02009339 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009340 font = TRUE;
9341 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009342 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009343 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9344 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009345 if (modec == 'g')
9346 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009347# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009348# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009349 /* return font name */
9350 if (font)
9351 return HL_TABLE()[id - 1].sg_font_name;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009352# endif
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009353
Bram Moolenaar071d4272004-06-13 20:20:40 +00009354 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009355 if ((USE_24BIT) && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009356 {
9357 guicolor_T color;
9358 long_u rgb;
9359 static char_u buf[10];
9360
9361 if (fg)
9362 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009363 else if (sp)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009364# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009365 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009366# else
9367 color = INVALCOLOR;
9368# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009369 else
9370 color = HL_TABLE()[id - 1].sg_gui_bg;
9371 if (color == INVALCOLOR)
9372 return NULL;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009373 rgb = (long_u)GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009374 sprintf((char *)buf, "#%02x%02x%02x",
9375 (unsigned)(rgb >> 16),
9376 (unsigned)(rgb >> 8) & 255,
9377 (unsigned)rgb & 255);
9378 return buf;
9379 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009380# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009381 if (fg)
9382 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009383 if (sp)
9384 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009385 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9386 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009387 if (font || sp)
9388 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009389 if (modec == 'c')
9390 {
9391 if (fg)
9392 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9393 else
9394 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009395 if (n < 0)
9396 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009397 sprintf((char *)name, "%d", n);
9398 return name;
9399 }
9400 /* term doesn't have color */
9401 return NULL;
9402}
9403#endif
9404
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009405#if (defined(FEAT_SYN_HL) \
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009406 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009407 && defined(FEAT_PRINTER)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009408/*
9409 * Return color name of highlight group "id" as RGB value.
9410 */
9411 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009412highlight_gui_color_rgb(
9413 int id,
9414 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009415{
9416 guicolor_T color;
9417
9418 if (id <= 0 || id > highlight_ga.ga_len)
9419 return 0L;
9420
9421 if (fg)
9422 color = HL_TABLE()[id - 1].sg_gui_fg;
9423 else
9424 color = HL_TABLE()[id - 1].sg_gui_bg;
9425
9426 if (color == INVALCOLOR)
9427 return 0L;
9428
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009429 return GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009430}
9431#endif
9432
9433/*
9434 * Output the syntax list header.
9435 * Return TRUE when started a new line.
9436 */
9437 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009438syn_list_header(
9439 int did_header, /* did header already */
9440 int outlen, /* length of string that comes */
9441 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009442{
9443 int endcol = 19;
9444 int newline = TRUE;
9445
9446 if (!did_header)
9447 {
9448 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009449 if (got_int)
9450 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009451 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9452 endcol = 15;
9453 }
9454 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009455 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009456 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009457 if (got_int)
9458 return TRUE;
9459 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009460 else
9461 {
9462 if (msg_col >= endcol) /* wrap around is like starting a new line */
9463 newline = FALSE;
9464 }
9465
9466 if (msg_col >= endcol) /* output at least one space */
9467 endcol = msg_col + 1;
9468 if (Columns <= endcol) /* avoid hang for tiny window */
9469 endcol = Columns - 1;
9470
9471 msg_advance(endcol);
9472
9473 /* Show "xxx" with the attributes. */
9474 if (!did_header)
9475 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01009476 msg_puts_attr("xxx", syn_id2attr(id));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009477 msg_putchar(' ');
9478 }
9479
9480 return newline;
9481}
9482
9483/*
9484 * Set the attribute numbers for a highlight group.
9485 * Called after one of the attributes has changed.
9486 */
9487 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009488set_hl_attr(
9489 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009490{
9491 attrentry_T at_en;
9492 struct hl_group *sgp = HL_TABLE() + idx;
9493
9494 /* The "Normal" group doesn't need an attribute number */
9495 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9496 return;
9497
9498#ifdef FEAT_GUI
9499 /*
9500 * For the GUI mode: If there are other than "normal" highlighting
9501 * attributes, need to allocate an attr number.
9502 */
9503 if (sgp->sg_gui_fg == INVALCOLOR
9504 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009505 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009506 && sgp->sg_font == NOFONT
9507# ifdef FEAT_XFONTSET
9508 && sgp->sg_fontset == NOFONTSET
9509# endif
9510 )
9511 {
9512 sgp->sg_gui_attr = sgp->sg_gui;
9513 }
9514 else
9515 {
9516 at_en.ae_attr = sgp->sg_gui;
9517 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9518 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009519 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009520 at_en.ae_u.gui.font = sgp->sg_font;
9521# ifdef FEAT_XFONTSET
9522 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9523# endif
9524 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9525 }
9526#endif
9527 /*
9528 * For the term mode: If there are other than "normal" highlighting
9529 * attributes, need to allocate an attr number.
9530 */
9531 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9532 sgp->sg_term_attr = sgp->sg_term;
9533 else
9534 {
9535 at_en.ae_attr = sgp->sg_term;
9536 at_en.ae_u.term.start = sgp->sg_start;
9537 at_en.ae_u.term.stop = sgp->sg_stop;
9538 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9539 }
9540
9541 /*
9542 * For the color term mode: If there are other than "normal"
9543 * highlighting attributes, need to allocate an attr number.
9544 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009545 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009546# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009547 && sgp->sg_gui_fg == INVALCOLOR
9548 && sgp->sg_gui_bg == INVALCOLOR
9549# endif
9550 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00009551 sgp->sg_cterm_attr = sgp->sg_cterm;
9552 else
9553 {
9554 at_en.ae_attr = sgp->sg_cterm;
9555 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9556 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009557# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar4f974752019-02-17 17:44:42 +01009558# ifdef MSWIN
Bram Moolenaarcafafb32018-02-22 21:07:09 +01009559 {
9560 int id;
9561 guicolor_T fg, bg;
9562
9563 id = syn_name2id((char_u *)"Normal");
9564 if (id > 0)
9565 {
9566 syn_id2colors(id, &fg, &bg);
9567 if (sgp->sg_gui_fg == INVALCOLOR)
9568 sgp->sg_gui_fg = fg;
9569 if (sgp->sg_gui_bg == INVALCOLOR)
9570 sgp->sg_gui_bg = bg;
9571 }
9572
9573 }
9574# endif
Bram Moolenaar187147a2016-05-01 13:09:57 +02009575 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
9576 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaard4fc5772018-02-27 14:39:03 +01009577 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
9578 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
9579 {
9580 /* If both fg and bg are invalid fall back to the cterm colors.
9581 * Helps when the GUI only uses an attribute, e.g. undercurl. */
9582 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
9583 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
9584 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009585# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009586 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9587 }
9588}
9589
9590/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009591 * Lookup a highlight group name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009592 * If it is not found, 0 is returned.
9593 */
9594 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009595syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009596{
9597 int i;
9598 char_u name_u[200];
9599
9600 /* Avoid using stricmp() too much, it's slow on some systems */
9601 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9602 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009603 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009604 vim_strup(name_u);
9605 for (i = highlight_ga.ga_len; --i >= 0; )
9606 if (HL_TABLE()[i].sg_name_u != NULL
9607 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9608 break;
9609 return i + 1;
9610}
9611
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02009612/*
9613 * Lookup a highlight group name and return its attributes.
9614 * Return zero if not found.
9615 */
9616 int
9617syn_name2attr(char_u *name)
9618{
9619 int id = syn_name2id(name);
9620
9621 if (id != 0)
Bram Moolenaar76301952017-09-22 13:53:37 +02009622 return syn_id2attr(id);
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02009623 return 0;
9624}
9625
Bram Moolenaar071d4272004-06-13 20:20:40 +00009626#if defined(FEAT_EVAL) || defined(PROTO)
9627/*
9628 * Return TRUE if highlight group "name" exists.
9629 */
9630 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009631highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009632{
9633 return (syn_name2id(name) > 0);
9634}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009635
9636# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9637/*
9638 * Return the name of highlight group "id".
9639 * When not a valid ID return an empty string.
9640 */
9641 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009642syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009643{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009644 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009645 return (char_u *)"";
9646 return HL_TABLE()[id - 1].sg_name;
9647}
9648# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009649#endif
9650
9651/*
9652 * Like syn_name2id(), but take a pointer + length argument.
9653 */
9654 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009655syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009656{
9657 char_u *name;
9658 int id = 0;
9659
9660 name = vim_strnsave(linep, len);
9661 if (name != NULL)
9662 {
9663 id = syn_name2id(name);
9664 vim_free(name);
9665 }
9666 return id;
9667}
9668
9669/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009670 * Find highlight group name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009671 * The argument is a pointer to the name and the length of the name.
9672 * If it doesn't exist yet, a new entry is created.
9673 * Return 0 for failure.
9674 */
9675 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009676syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009677{
9678 int id;
9679 char_u *name;
9680
9681 name = vim_strnsave(pp, len);
9682 if (name == NULL)
9683 return 0;
9684
9685 id = syn_name2id(name);
9686 if (id == 0) /* doesn't exist yet */
9687 id = syn_add_group(name);
9688 else
9689 vim_free(name);
9690 return id;
9691}
9692
9693/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009694 * Add new highlight group and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009695 * "name" must be an allocated string, it will be consumed.
9696 * Return 0 for failure.
9697 */
9698 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009699syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009700{
9701 char_u *p;
9702
9703 /* Check that the name is ASCII letters, digits and underscore. */
9704 for (p = name; *p != NUL; ++p)
9705 {
9706 if (!vim_isprintc(*p))
9707 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01009708 emsg(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009709 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009710 return 0;
9711 }
9712 else if (!ASCII_ISALNUM(*p) && *p != '_')
9713 {
9714 /* This is an error, but since there previously was no check only
9715 * give a warning. */
Bram Moolenaar8820b482017-03-16 17:23:31 +01009716 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar32526b32019-01-19 17:43:09 +01009717 msg(_("W18: Invalid character in group name"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009718 break;
9719 }
9720 }
9721
9722 /*
9723 * First call for this growarray: init growing array.
9724 */
9725 if (highlight_ga.ga_data == NULL)
9726 {
9727 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9728 highlight_ga.ga_growsize = 10;
9729 }
9730
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009731 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009732 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01009733 emsg(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009734 vim_free(name);
9735 return 0;
9736 }
9737
Bram Moolenaar071d4272004-06-13 20:20:40 +00009738 /*
9739 * Make room for at least one other syntax_highlight entry.
9740 */
9741 if (ga_grow(&highlight_ga, 1) == FAIL)
9742 {
9743 vim_free(name);
9744 return 0;
9745 }
9746
9747 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9748 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9749 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009750#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009751 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9752 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009753# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009754 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009755# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009756#endif
9757 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009758
9759 return highlight_ga.ga_len; /* ID is index plus one */
9760}
9761
9762/*
9763 * When, just after calling syn_add_group(), an error is discovered, this
9764 * function deletes the new name.
9765 */
9766 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009767syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009768{
9769 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009770 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9771 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9772}
9773
9774/*
9775 * Translate a group ID to highlight attributes.
9776 */
9777 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009778syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009779{
9780 int attr;
9781 struct hl_group *sgp;
9782
9783 hl_id = syn_get_final_id(hl_id);
9784 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9785
9786#ifdef FEAT_GUI
9787 /*
9788 * Only use GUI attr when the GUI is being used.
9789 */
9790 if (gui.in_use)
9791 attr = sgp->sg_gui_attr;
9792 else
9793#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009794 if (IS_CTERM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009795 attr = sgp->sg_cterm_attr;
9796 else
9797 attr = sgp->sg_term_attr;
9798
9799 return attr;
9800}
9801
Bram Moolenaarc71053c2017-09-14 00:00:44 +02009802#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009803/*
9804 * Get the GUI colors and attributes for a group ID.
9805 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9806 */
9807 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009808syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009809{
9810 struct hl_group *sgp;
9811
9812 hl_id = syn_get_final_id(hl_id);
9813 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9814
9815 *fgp = sgp->sg_gui_fg;
9816 *bgp = sgp->sg_gui_bg;
9817 return sgp->sg_gui;
9818}
9819#endif
9820
Bram Moolenaar4f974752019-02-17 17:44:42 +01009821#if (defined(MSWIN) \
Bram Moolenaarafde13b2019-04-28 19:46:49 +02009822 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
Bram Moolenaara772baf2018-05-20 13:35:44 +02009823 && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
Bram Moolenaarc71053c2017-09-14 00:00:44 +02009824 void
9825syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
9826{
9827 struct hl_group *sgp;
9828
9829 hl_id = syn_get_final_id(hl_id);
9830 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9831 *fgp = sgp->sg_cterm_fg - 1;
9832 *bgp = sgp->sg_cterm_bg - 1;
9833}
9834#endif
9835
Bram Moolenaar071d4272004-06-13 20:20:40 +00009836/*
9837 * Translate a group ID to the final group ID (following links).
9838 */
9839 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009840syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009841{
9842 int count;
9843 struct hl_group *sgp;
9844
9845 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9846 return 0; /* Can be called from eval!! */
9847
9848 /*
9849 * Follow links until there is no more.
9850 * Look out for loops! Break after 100 links.
9851 */
9852 for (count = 100; --count >= 0; )
9853 {
9854 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9855 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9856 break;
9857 hl_id = sgp->sg_link;
9858 }
9859
9860 return hl_id;
9861}
9862
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01009863#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009864/*
9865 * Call this function just after the GUI has started.
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01009866 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009867 * It finds the font and color handles for the highlighting groups.
9868 */
9869 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009870highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009871{
9872 int idx;
9873
9874 /* First get the colors from the "Normal" and "Menu" group, if set */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009875 if (USE_24BIT)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009876 set_normal_colors();
Bram Moolenaar071d4272004-06-13 20:20:40 +00009877
9878 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9879 gui_do_one_color(idx, FALSE, FALSE);
9880
9881 highlight_changed();
9882}
9883
9884 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009885gui_do_one_color(
9886 int idx,
Bram Moolenaar380130f2016-04-22 11:24:43 +02009887 int do_menu UNUSED, /* TRUE: might set the menu font */
9888 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009889{
9890 int didit = FALSE;
9891
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009892# ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009893# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009894 if (gui.in_use)
9895# endif
9896 if (HL_TABLE()[idx].sg_font_name != NULL)
9897 {
9898 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009899 do_tooltip, TRUE);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009900 didit = TRUE;
9901 }
9902# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009903 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9904 {
9905 HL_TABLE()[idx].sg_gui_fg =
9906 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9907 didit = TRUE;
9908 }
9909 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9910 {
9911 HL_TABLE()[idx].sg_gui_bg =
9912 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9913 didit = TRUE;
9914 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009915# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009916 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9917 {
9918 HL_TABLE()[idx].sg_gui_sp =
9919 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9920 didit = TRUE;
9921 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009922# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009923 if (didit) /* need to get a new attr number */
9924 set_hl_attr(idx);
9925}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009926#endif
9927
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009928#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
9929/*
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02009930 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009931 */
9932 static void
9933combine_stl_hlt(
9934 int id,
9935 int id_S,
9936 int id_alt,
9937 int hlcnt,
9938 int i,
9939 int hlf,
9940 int *table)
9941{
9942 struct hl_group *hlt = HL_TABLE();
9943
9944 if (id_alt == 0)
9945 {
9946 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
9947 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
9948 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
9949# if defined(FEAT_GUI) || defined(FEAT_EVAL)
9950 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
9951# endif
9952 }
9953 else
9954 mch_memmove(&hlt[hlcnt + i],
9955 &hlt[id_alt - 1],
9956 sizeof(struct hl_group));
9957 hlt[hlcnt + i].sg_link = 0;
9958
9959 hlt[hlcnt + i].sg_term ^=
9960 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9961 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9962 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9963 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9964 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9965 hlt[hlcnt + i].sg_cterm ^=
9966 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9967 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9968 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9969 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9970 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
9971# if defined(FEAT_GUI) || defined(FEAT_EVAL)
9972 hlt[hlcnt + i].sg_gui ^=
9973 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
9974# endif
9975# ifdef FEAT_GUI
9976 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9977 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9978 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9979 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
9980 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9981 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
9982 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9983 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9984# ifdef FEAT_XFONTSET
9985 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9986 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9987# endif
9988# endif
9989 highlight_ga.ga_len = hlcnt + i + 1;
9990 set_hl_attr(hlcnt + i); /* At long last we can apply */
9991 table[i] = syn_id2attr(hlcnt + i + 1);
9992}
9993#endif
9994
Bram Moolenaar071d4272004-06-13 20:20:40 +00009995/*
9996 * Translate the 'highlight' option into attributes in highlight_attr[] and
9997 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9998 * corresponding highlights to use on top of HLF_SNC is computed.
9999 * Called only when the 'highlight' option has been changed and upon first
10000 * screen redraw after any :highlight command.
10001 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
10002 */
10003 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010004highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010005{
10006 int hlf;
10007 int i;
10008 char_u *p;
10009 int attr;
10010 char_u *end;
10011 int id;
10012#ifdef USER_HIGHLIGHT
Bram Moolenaar54315892019-04-26 22:33:49 +020010013 char_u userhl[30]; // use 30 to avoid compiler warning
Bram Moolenaar071d4272004-06-13 20:20:40 +000010014# ifdef FEAT_STL_OPT
Bram Moolenaar071d4272004-06-13 20:20:40 +000010015 int id_S = -1;
Bram Moolenaar61859032018-03-20 13:00:25 +010010016 int id_SNC = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010017# ifdef FEAT_TERMINAL
Bram Moolenaar61859032018-03-20 13:00:25 +010010018 int id_ST = 0;
10019 int id_STNC = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010020# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010021 int hlcnt;
10022# endif
10023#endif
10024 static int hl_flags[HLF_COUNT] = HL_FLAGS;
10025
10026 need_highlight_changed = FALSE;
10027
10028 /*
10029 * Clear all attributes.
10030 */
10031 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
10032 highlight_attr[hlf] = 0;
10033
10034 /*
10035 * First set all attributes to their default value.
10036 * Then use the attributes from the 'highlight' option.
10037 */
10038 for (i = 0; i < 2; ++i)
10039 {
10040 if (i)
10041 p = p_hl;
10042 else
10043 p = get_highlight_default();
10044 if (p == NULL) /* just in case */
10045 continue;
10046
10047 while (*p)
10048 {
10049 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
10050 if (hl_flags[hlf] == *p)
10051 break;
10052 ++p;
10053 if (hlf == (int)HLF_COUNT || *p == NUL)
10054 return FAIL;
10055
10056 /*
10057 * Allow several hl_flags to be combined, like "bu" for
10058 * bold-underlined.
10059 */
10060 attr = 0;
10061 for ( ; *p && *p != ','; ++p) /* parse upto comma */
10062 {
Bram Moolenaar1c465442017-03-12 20:10:05 +010010063 if (VIM_ISWHITE(*p)) /* ignore white space */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010064 continue;
10065
10066 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
10067 return FAIL;
10068
10069 switch (*p)
10070 {
10071 case 'b': attr |= HL_BOLD;
10072 break;
10073 case 'i': attr |= HL_ITALIC;
10074 break;
10075 case '-':
10076 case 'n': /* no highlighting */
10077 break;
10078 case 'r': attr |= HL_INVERSE;
10079 break;
10080 case 's': attr |= HL_STANDOUT;
10081 break;
10082 case 'u': attr |= HL_UNDERLINE;
10083 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +000010084 case 'c': attr |= HL_UNDERCURL;
10085 break;
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +020010086 case 't': attr |= HL_STRIKETHROUGH;
10087 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010088 case ':': ++p; /* highlight group name */
10089 if (attr || *p == NUL) /* no combinations */
10090 return FAIL;
10091 end = vim_strchr(p, ',');
10092 if (end == NULL)
10093 end = p + STRLEN(p);
10094 id = syn_check_group(p, (int)(end - p));
10095 if (id == 0)
10096 return FAIL;
10097 attr = syn_id2attr(id);
10098 p = end - 1;
10099#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
10100 if (hlf == (int)HLF_SNC)
10101 id_SNC = syn_get_final_id(id);
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010102# ifdef FEAT_TERMINAL
10103 else if (hlf == (int)HLF_ST)
10104 id_ST = syn_get_final_id(id);
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010105 else if (hlf == (int)HLF_STNC)
10106 id_STNC = syn_get_final_id(id);
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010107# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010108 else if (hlf == (int)HLF_S)
10109 id_S = syn_get_final_id(id);
10110#endif
10111 break;
10112 default: return FAIL;
10113 }
10114 }
10115 highlight_attr[hlf] = attr;
10116
10117 p = skip_to_option_part(p); /* skip comma and spaces */
10118 }
10119 }
10120
10121#ifdef USER_HIGHLIGHT
10122 /* Setup the user highlights
10123 *
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010124 * Temporarily utilize 28 more hl entries:
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010125 * 9 for User1-User9 combined with StatusLineNC
10126 * 9 for User1-User9 combined with StatusLineTerm
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010127 * 9 for User1-User9 combined with StatusLineTermNC
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010128 * 1 for StatusLine default
10129 * Have to be in there simultaneously in case of table overflows in
10130 * get_attr_entry()
Bram Moolenaar071d4272004-06-13 20:20:40 +000010131 */
10132# ifdef FEAT_STL_OPT
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010133 if (ga_grow(&highlight_ga, 28) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010134 return FAIL;
10135 hlcnt = highlight_ga.ga_len;
Bram Moolenaard6a7b3e2017-08-19 21:35:35 +020010136 if (id_S == -1)
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010137 {
10138 /* Make sure id_S is always valid to simplify code below. Use the last
10139 * entry. */
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010140 vim_memset(&HL_TABLE()[hlcnt + 27], 0, sizeof(struct hl_group));
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010141 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
10142 id_S = hlcnt + 19;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010143 }
10144# endif
10145 for (i = 0; i < 9; i++)
10146 {
10147 sprintf((char *)userhl, "User%d", i + 1);
10148 id = syn_name2id(userhl);
10149 if (id == 0)
10150 {
10151 highlight_user[i] = 0;
10152# ifdef FEAT_STL_OPT
10153 highlight_stlnc[i] = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010154# ifdef FEAT_TERMINAL
10155 highlight_stlterm[i] = 0;
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010156 highlight_stltermnc[i] = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010157# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010158# endif
10159 }
10160 else
10161 {
Bram Moolenaar071d4272004-06-13 20:20:40 +000010162 highlight_user[i] = syn_id2attr(id);
10163# ifdef FEAT_STL_OPT
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010164 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
10165 HLF_SNC, highlight_stlnc);
10166# ifdef FEAT_TERMINAL
10167 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
10168 HLF_ST, highlight_stlterm);
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010169 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
10170 HLF_STNC, highlight_stltermnc);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010171# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010172# endif
10173 }
10174 }
10175# ifdef FEAT_STL_OPT
10176 highlight_ga.ga_len = hlcnt;
10177# endif
10178
10179#endif /* USER_HIGHLIGHT */
10180
10181 return OK;
10182}
10183
Bram Moolenaar4f688582007-07-24 12:34:30 +000010184#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010185
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010010186static void highlight_list(void);
10187static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010188
10189/*
10190 * Handle command line completion for :highlight command.
10191 */
10192 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010193set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010194{
10195 char_u *p;
10196
10197 /* Default: expand group names */
10198 xp->xp_context = EXPAND_HIGHLIGHT;
10199 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +000010200 include_link = 2;
10201 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010202
10203 /* (part of) subcommand already typed */
10204 if (*arg != NUL)
10205 {
10206 p = skiptowhite(arg);
10207 if (*p != NUL) /* past "default" or group name */
10208 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010209 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010210 if (STRNCMP("default", arg, p - arg) == 0)
10211 {
10212 arg = skipwhite(p);
10213 xp->xp_pattern = arg;
10214 p = skiptowhite(arg);
10215 }
10216 if (*p != NUL) /* past group name */
10217 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010218 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010219 if (arg[1] == 'i' && arg[0] == 'N')
10220 highlight_list();
10221 if (STRNCMP("link", arg, p - arg) == 0
10222 || STRNCMP("clear", arg, p - arg) == 0)
10223 {
10224 xp->xp_pattern = skipwhite(p);
10225 p = skiptowhite(xp->xp_pattern);
10226 if (*p != NUL) /* past first group name */
10227 {
10228 xp->xp_pattern = skipwhite(p);
10229 p = skiptowhite(xp->xp_pattern);
10230 }
10231 }
10232 if (*p != NUL) /* past group name(s) */
10233 xp->xp_context = EXPAND_NOTHING;
10234 }
10235 }
10236 }
10237}
10238
10239/*
10240 * List highlighting matches in a nice way.
10241 */
10242 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010243highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010244{
10245 int i;
10246
10247 for (i = 10; --i >= 0; )
Bram Moolenaar8820b482017-03-16 17:23:31 +010010248 highlight_list_two(i, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +000010249 for (i = 40; --i >= 0; )
10250 highlight_list_two(99, 0);
10251}
10252
10253 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010254highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010255{
Bram Moolenaar32526b32019-01-19 17:43:09 +010010256 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010257 msg_clr_eos();
10258 out_flush();
10259 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
10260}
10261
10262#endif /* FEAT_CMDL_COMPL */
10263
10264#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
10265 || defined(FEAT_SIGNS) || defined(PROTO)
10266/*
10267 * Function given to ExpandGeneric() to obtain the list of group names.
Bram Moolenaar071d4272004-06-13 20:20:40 +000010268 */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010269 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010270get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010271{
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010272 return get_highlight_name_ext(xp, idx, TRUE);
10273}
10274
10275/*
10276 * Obtain a highlight group name.
10277 * When "skip_cleared" is TRUE don't return a cleared entry.
10278 */
10279 char_u *
10280get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
10281{
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010282 if (idx < 0)
10283 return NULL;
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010284
10285 /* Items are never removed from the table, skip the ones that were
10286 * cleared. */
10287 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
10288 return (char_u *)"";
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010289
Bram Moolenaar071d4272004-06-13 20:20:40 +000010290#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000010291 if (idx == highlight_ga.ga_len && include_none != 0)
10292 return (char_u *)"none";
10293 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010294 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +000010295 if (idx == highlight_ga.ga_len + include_none + include_default
10296 && include_link != 0)
10297 return (char_u *)"link";
10298 if (idx == highlight_ga.ga_len + include_none + include_default + 1
10299 && include_link != 0)
10300 return (char_u *)"clear";
10301#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +010010302 if (idx >= highlight_ga.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010303 return NULL;
10304 return HL_TABLE()[idx].sg_name;
10305}
10306#endif
10307
Bram Moolenaar4f688582007-07-24 12:34:30 +000010308#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010309/*
10310 * Free all the highlight group fonts.
10311 * Used when quitting for systems which need it.
10312 */
10313 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010314free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010315{
10316 int idx;
10317
10318 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
10319 {
10320 gui_mch_free_font(HL_TABLE()[idx].sg_font);
10321 HL_TABLE()[idx].sg_font = NOFONT;
10322# ifdef FEAT_XFONTSET
10323 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
10324 HL_TABLE()[idx].sg_fontset = NOFONTSET;
10325# endif
10326 }
10327
10328 gui_mch_free_font(gui.norm_font);
10329# ifdef FEAT_XFONTSET
10330 gui_mch_free_fontset(gui.fontset);
10331# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +020010332# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +000010333 gui_mch_free_font(gui.bold_font);
10334 gui_mch_free_font(gui.ital_font);
10335 gui_mch_free_font(gui.boldital_font);
10336# endif
10337}
10338#endif
10339
10340/**************************************
10341 * End of Highlighting stuff *
10342 **************************************/