blob: 0c739476e23dc700d2751d9b9a7812b55bb1393c [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#ifdef FEAT_MBYTE
1978 - (has_mbyte
1979 ? (*mb_head_off)(line, line + current_col - 1)
1980 : 0)
1981#endif
1982 , syn_buf)))
1983 {
1984 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001985 &endcol, &flags, &next_list, cur_si,
1986 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001987 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001988 {
1989 if (push_current_state(KEYWORD_IDX) == OK)
1990 {
1991 cur_si = &CUR_STATE(current_state.ga_len - 1);
1992 cur_si->si_m_startcol = current_col;
1993 cur_si->si_h_startpos.lnum = current_lnum;
1994 cur_si->si_h_startpos.col = 0; /* starts right away */
1995 cur_si->si_m_endpos.lnum = current_lnum;
1996 cur_si->si_m_endpos.col = endcol;
1997 cur_si->si_h_endpos.lnum = current_lnum;
1998 cur_si->si_h_endpos.col = endcol;
1999 cur_si->si_ends = TRUE;
2000 cur_si->si_end_idx = 0;
2001 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002002#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002003 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002004 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002005 if (current_state.ga_len > 1)
2006 cur_si->si_flags |=
2007 CUR_STATE(current_state.ga_len - 2).si_flags
2008 & HL_CONCEAL;
2009#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002010 cur_si->si_id = syn_id;
2011 cur_si->si_trans_id = syn_id;
2012 if (flags & HL_TRANSP)
2013 {
2014 if (current_state.ga_len < 2)
2015 {
2016 cur_si->si_attr = 0;
2017 cur_si->si_trans_id = 0;
2018 }
2019 else
2020 {
2021 cur_si->si_attr = CUR_STATE(
2022 current_state.ga_len - 2).si_attr;
2023 cur_si->si_trans_id = CUR_STATE(
2024 current_state.ga_len - 2).si_trans_id;
2025 }
2026 }
2027 else
2028 cur_si->si_attr = syn_id2attr(syn_id);
2029 cur_si->si_cont_list = NULL;
2030 cur_si->si_next_list = next_list;
2031 check_keepend();
2032 }
2033 else
2034 vim_free(next_list);
2035 }
2036 }
2037 }
2038
2039 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002040 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002041 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002042 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002043 {
2044 /*
2045 * If we didn't check for a match yet, or we are past it, check
2046 * for any match with a pattern.
2047 */
2048 if (next_match_idx < 0 || next_match_col < (int)current_col)
2049 {
2050 /*
2051 * Check all relevant patterns for a match at this
2052 * position. This is complicated, because matching with a
2053 * pattern takes quite a bit of time, thus we want to
2054 * avoid doing it when it's not needed.
2055 */
2056 next_match_idx = 0; /* no match in this line yet */
2057 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002058 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002059 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002060 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002061 if ( spp->sp_syncing == syncing
2062 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2063 && (spp->sp_type == SPTYPE_MATCH
2064 || spp->sp_type == SPTYPE_START)
2065 && (current_next_list != NULL
2066 ? in_id_list(NULL, current_next_list,
2067 &spp->sp_syn, 0)
2068 : (cur_si == NULL
2069 ? !(spp->sp_flags & HL_CONTAINED)
2070 : in_id_list(cur_si,
2071 cur_si->si_cont_list, &spp->sp_syn,
2072 spp->sp_flags & HL_CONTAINED))))
2073 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002074 int r;
2075
Bram Moolenaar071d4272004-06-13 20:20:40 +00002076 /* If we already tried matching in this line, and
2077 * there isn't a match before next_match_col, skip
2078 * this item. */
2079 if (spp->sp_line_id == current_line_id
2080 && spp->sp_startcol >= next_match_col)
2081 continue;
2082 spp->sp_line_id = current_line_id;
2083
2084 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2085 if (lc_col < 0)
2086 lc_col = 0;
2087
2088 regmatch.rmm_ic = spp->sp_ic;
2089 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002090 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002091 current_lnum,
2092 (colnr_T)lc_col,
Bram Moolenaard23a8232018-02-10 18:45:26 +01002093 IF_SYN_TIME(&spp->sp_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002094 spp->sp_prog = regmatch.regprog;
2095 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002096 {
2097 /* no match in this line, try another one */
2098 spp->sp_startcol = MAXCOL;
2099 continue;
2100 }
2101
2102 /*
2103 * Compute the first column of the match.
2104 */
2105 syn_add_start_off(&pos, &regmatch,
2106 spp, SPO_MS_OFF, -1);
2107 if (pos.lnum > current_lnum)
2108 {
2109 /* must have used end of match in a next line,
2110 * we can't handle that */
2111 spp->sp_startcol = MAXCOL;
2112 continue;
2113 }
2114 startcol = pos.col;
2115
2116 /* remember the next column where this pattern
2117 * matches in the current line */
2118 spp->sp_startcol = startcol;
2119
2120 /*
2121 * If a previously found match starts at a lower
2122 * column number, don't use this one.
2123 */
2124 if (startcol >= next_match_col)
2125 continue;
2126
2127 /*
2128 * If we matched this pattern at this position
2129 * before, skip it. Must retry in the next
2130 * column, because it may match from there.
2131 */
2132 if (did_match_already(idx, &zero_width_next_ga))
2133 {
2134 try_next_column = TRUE;
2135 continue;
2136 }
2137
2138 endpos.lnum = regmatch.endpos[0].lnum;
2139 endpos.col = regmatch.endpos[0].col;
2140
2141 /* Compute the highlight start. */
2142 syn_add_start_off(&hl_startpos, &regmatch,
2143 spp, SPO_HS_OFF, -1);
2144
2145 /* Compute the region start. */
2146 /* Default is to use the end of the match. */
2147 syn_add_end_off(&eos_pos, &regmatch,
2148 spp, SPO_RS_OFF, 0);
2149
2150 /*
2151 * Grab the external submatches before they get
2152 * overwritten. Reference count doesn't change.
2153 */
2154 unref_extmatch(cur_extmatch);
2155 cur_extmatch = re_extmatch_out;
2156 re_extmatch_out = NULL;
2157
2158 flags = 0;
2159 eoe_pos.lnum = 0; /* avoid warning */
2160 eoe_pos.col = 0;
2161 end_idx = 0;
2162 hl_endpos.lnum = 0;
2163
2164 /*
2165 * For a "oneline" the end must be found in the
2166 * same line too. Search for it after the end of
2167 * the match with the start pattern. Set the
2168 * resulting end positions at the same time.
2169 */
2170 if (spp->sp_type == SPTYPE_START
2171 && (spp->sp_flags & HL_ONELINE))
2172 {
2173 lpos_T startpos;
2174
2175 startpos = endpos;
2176 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2177 &flags, &eoe_pos, &end_idx, cur_extmatch);
2178 if (endpos.lnum == 0)
2179 continue; /* not found */
2180 }
2181
2182 /*
2183 * For a "match" the size must be > 0 after the
2184 * end offset needs has been added. Except when
2185 * syncing.
2186 */
2187 else if (spp->sp_type == SPTYPE_MATCH)
2188 {
2189 syn_add_end_off(&hl_endpos, &regmatch, spp,
2190 SPO_HE_OFF, 0);
2191 syn_add_end_off(&endpos, &regmatch, spp,
2192 SPO_ME_OFF, 0);
2193 if (endpos.lnum == current_lnum
2194 && (int)endpos.col + syncing < startcol)
2195 {
2196 /*
2197 * If an empty string is matched, may need
2198 * to try matching again at next column.
2199 */
2200 if (regmatch.startpos[0].col
2201 == regmatch.endpos[0].col)
2202 try_next_column = TRUE;
2203 continue;
2204 }
2205 }
2206
2207 /*
2208 * keep the best match so far in next_match_*
2209 */
2210 /* Highlighting must start after startpos and end
2211 * before endpos. */
2212 if (hl_startpos.lnum == current_lnum
2213 && (int)hl_startpos.col < startcol)
2214 hl_startpos.col = startcol;
2215 limit_pos_zero(&hl_endpos, &endpos);
2216
2217 next_match_idx = idx;
2218 next_match_col = startcol;
2219 next_match_m_endpos = endpos;
2220 next_match_h_endpos = hl_endpos;
2221 next_match_h_startpos = hl_startpos;
2222 next_match_flags = flags;
2223 next_match_eos_pos = eos_pos;
2224 next_match_eoe_pos = eoe_pos;
2225 next_match_end_idx = end_idx;
2226 unref_extmatch(next_match_extmatch);
2227 next_match_extmatch = cur_extmatch;
2228 cur_extmatch = NULL;
2229 }
2230 }
2231 }
2232
2233 /*
2234 * If we found a match at the current column, use it.
2235 */
2236 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2237 {
2238 synpat_T *lspp;
2239
2240 /* When a zero-width item matched which has a nextgroup,
2241 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002242 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002243 if (next_match_m_endpos.lnum == current_lnum
2244 && next_match_m_endpos.col == current_col
2245 && lspp->sp_next_list != NULL)
2246 {
2247 current_next_list = lspp->sp_next_list;
2248 current_next_flags = lspp->sp_flags;
2249 keep_next_list = TRUE;
2250 zero_width_next_list = TRUE;
2251
2252 /* Add the index to a list, so that we can check
2253 * later that we don't match it again (and cause an
2254 * endless loop). */
2255 if (ga_grow(&zero_width_next_ga, 1) == OK)
2256 {
2257 ((int *)(zero_width_next_ga.ga_data))
2258 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002259 }
2260 next_match_idx = -1;
2261 }
2262 else
2263 cur_si = push_next_match(cur_si);
2264 found_match = TRUE;
2265 }
2266 }
2267 }
2268
2269 /*
2270 * Handle searching for nextgroup match.
2271 */
2272 if (current_next_list != NULL && !keep_next_list)
2273 {
2274 /*
2275 * If a nextgroup was not found, continue looking for one if:
2276 * - this is an empty line and the "skipempty" option was given
2277 * - we are on white space and the "skipwhite" option was given
2278 */
2279 if (!found_match)
2280 {
2281 line = syn_getcurline();
2282 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002283 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002284 || ((current_next_flags & HL_SKIPEMPTY)
2285 && *line == NUL))
2286 break;
2287 }
2288
2289 /*
2290 * If a nextgroup was found: Use it, and continue looking for
2291 * contained matches.
2292 * If a nextgroup was not found: Continue looking for a normal
2293 * match.
2294 * When did set current_next_list for a zero-width item and no
2295 * match was found don't loop (would get stuck).
2296 */
2297 current_next_list = NULL;
2298 next_match_idx = -1;
2299 if (!zero_width_next_list)
2300 found_match = TRUE;
2301 }
2302
2303 } while (found_match);
2304
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002305 restore_chartab(buf_chartab);
2306
Bram Moolenaar071d4272004-06-13 20:20:40 +00002307 /*
2308 * Use attributes from the current state, if within its highlighting.
2309 * If not, use attributes from the current-but-one state, etc.
2310 */
2311 current_attr = 0;
2312#ifdef FEAT_EVAL
2313 current_id = 0;
2314 current_trans_id = 0;
2315#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002316#ifdef FEAT_CONCEAL
2317 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02002318 current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002319#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002320 if (cur_si != NULL)
2321 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002322#ifndef FEAT_EVAL
2323 int current_trans_id = 0;
2324#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002325 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2326 {
2327 sip = &CUR_STATE(idx);
2328 if ((current_lnum > sip->si_h_startpos.lnum
2329 || (current_lnum == sip->si_h_startpos.lnum
2330 && current_col >= sip->si_h_startpos.col))
2331 && (sip->si_h_endpos.lnum == 0
2332 || current_lnum < sip->si_h_endpos.lnum
2333 || (current_lnum == sip->si_h_endpos.lnum
2334 && current_col < sip->si_h_endpos.col)))
2335 {
2336 current_attr = sip->si_attr;
2337#ifdef FEAT_EVAL
2338 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002339#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002340 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002341#ifdef FEAT_CONCEAL
2342 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002343 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002344 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002345#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002346 break;
2347 }
2348 }
2349
Bram Moolenaar217ad922005-03-20 22:37:15 +00002350 if (can_spell != NULL)
2351 {
2352 struct sp_syn sps;
2353
2354 /*
2355 * set "can_spell" to TRUE if spell checking is supposed to be
2356 * done in the current item.
2357 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002358 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002359 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002360 /* There is no @Spell cluster: Do spelling for items without
2361 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002362 if (syn_block->b_nospell_cluster_id == 0
2363 || current_trans_id == 0)
2364 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002365 else
2366 {
2367 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002368 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002369 sps.cont_in_list = NULL;
2370 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2371 }
2372 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002373 else
2374 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002375 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002376 * the @Spell cluster. But not when @NoSpell is also there.
2377 * At the toplevel only spell check when ":syn spell toplevel"
2378 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002379 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002380 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002381 else
2382 {
2383 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002384 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002385 sps.cont_in_list = NULL;
2386 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2387
Bram Moolenaar860cae12010-06-05 23:22:07 +02002388 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002389 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002390 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002391 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2392 *can_spell = FALSE;
2393 }
2394 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002395 }
2396 }
2397
2398
Bram Moolenaar071d4272004-06-13 20:20:40 +00002399 /*
2400 * Check for end of current state (and the states before it) at the
2401 * next column. Don't do this for syncing, because we would miss a
2402 * single character match.
2403 * First check if the current state ends at the current column. It
2404 * may be for an empty match and a containing item might end in the
2405 * current column.
2406 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002407 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002408 {
2409 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002410 if (current_state.ga_len > 0
2411 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002412 {
2413 ++current_col;
2414 check_state_ends();
2415 --current_col;
2416 }
2417 }
2418 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002419 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002420 /* Default: Only do spelling when there is no @Spell cluster or when
2421 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002422 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2423 ? (syn_block->b_spell_cluster_id == 0)
2424 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002425
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002426 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002427 if (current_next_list != NULL
Bram Moolenaar069dafc2018-03-03 20:02:19 +01002428 && (line = syn_getcurline())[current_col] != NUL
2429 && line[current_col + 1] == NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002430 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2431 current_next_list = NULL;
2432
2433 if (zero_width_next_ga.ga_len > 0)
2434 ga_clear(&zero_width_next_ga);
2435
2436 /* No longer need external matches. But keep next_match_extmatch. */
2437 unref_extmatch(re_extmatch_out);
2438 re_extmatch_out = NULL;
2439 unref_extmatch(cur_extmatch);
2440
2441 return current_attr;
2442}
2443
2444
2445/*
2446 * Check if we already matched pattern "idx" at the current column.
2447 */
2448 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002449did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002450{
2451 int i;
2452
2453 for (i = current_state.ga_len; --i >= 0; )
2454 if (CUR_STATE(i).si_m_startcol == (int)current_col
2455 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2456 && CUR_STATE(i).si_idx == idx)
2457 return TRUE;
2458
2459 /* Zero-width matches with a nextgroup argument are not put on the syntax
2460 * stack, and can only be matched once anyway. */
2461 for (i = gap->ga_len; --i >= 0; )
2462 if (((int *)(gap->ga_data))[i] == idx)
2463 return TRUE;
2464
2465 return FALSE;
2466}
2467
2468/*
2469 * Push the next match onto the stack.
2470 */
2471 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002472push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002473{
2474 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002475#ifdef FEAT_CONCEAL
2476 int save_flags;
2477#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002478
Bram Moolenaar860cae12010-06-05 23:22:07 +02002479 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002480
2481 /*
2482 * Push the item in current_state stack;
2483 */
2484 if (push_current_state(next_match_idx) == OK)
2485 {
2486 /*
2487 * If it's a start-skip-end type that crosses lines, figure out how
2488 * much it continues in this line. Otherwise just fill in the length.
2489 */
2490 cur_si = &CUR_STATE(current_state.ga_len - 1);
2491 cur_si->si_h_startpos = next_match_h_startpos;
2492 cur_si->si_m_startcol = current_col;
2493 cur_si->si_m_lnum = current_lnum;
2494 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002495#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002496 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002497 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002498 if (current_state.ga_len > 1)
2499 cur_si->si_flags |=
2500 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2501#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002502 cur_si->si_next_list = spp->sp_next_list;
2503 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2504 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2505 {
2506 /* Try to find the end pattern in the current line */
2507 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2508 check_keepend();
2509 }
2510 else
2511 {
2512 cur_si->si_m_endpos = next_match_m_endpos;
2513 cur_si->si_h_endpos = next_match_h_endpos;
2514 cur_si->si_ends = TRUE;
2515 cur_si->si_flags |= next_match_flags;
2516 cur_si->si_eoe_pos = next_match_eoe_pos;
2517 cur_si->si_end_idx = next_match_end_idx;
2518 }
2519 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2520 keepend_level = current_state.ga_len - 1;
2521 check_keepend();
2522 update_si_attr(current_state.ga_len - 1);
2523
Bram Moolenaar860cae12010-06-05 23:22:07 +02002524#ifdef FEAT_CONCEAL
2525 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2526#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002527 /*
2528 * If the start pattern has another highlight group, push another item
2529 * on the stack for the start pattern.
2530 */
2531 if ( spp->sp_type == SPTYPE_START
2532 && spp->sp_syn_match_id != 0
2533 && push_current_state(next_match_idx) == OK)
2534 {
2535 cur_si = &CUR_STATE(current_state.ga_len - 1);
2536 cur_si->si_h_startpos = next_match_h_startpos;
2537 cur_si->si_m_startcol = current_col;
2538 cur_si->si_m_lnum = current_lnum;
2539 cur_si->si_m_endpos = next_match_eos_pos;
2540 cur_si->si_h_endpos = next_match_eos_pos;
2541 cur_si->si_ends = TRUE;
2542 cur_si->si_end_idx = 0;
2543 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002544#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002545 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002546 cur_si->si_flags |= save_flags;
2547 if (cur_si->si_flags & HL_CONCEALENDS)
2548 cur_si->si_flags |= HL_CONCEAL;
2549#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002550 cur_si->si_next_list = NULL;
2551 check_keepend();
2552 update_si_attr(current_state.ga_len - 1);
2553 }
2554 }
2555
2556 next_match_idx = -1; /* try other match next time */
2557
2558 return cur_si;
2559}
2560
2561/*
2562 * Check for end of current state (and the states before it).
2563 */
2564 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002565check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002566{
2567 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002568 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002569
2570 cur_si = &CUR_STATE(current_state.ga_len - 1);
2571 for (;;)
2572 {
2573 if (cur_si->si_ends
2574 && (cur_si->si_m_endpos.lnum < current_lnum
2575 || (cur_si->si_m_endpos.lnum == current_lnum
2576 && cur_si->si_m_endpos.col <= current_col)))
2577 {
2578 /*
2579 * If there is an end pattern group ID, highlight the end pattern
2580 * now. No need to pop the current item from the stack.
2581 * Only do this if the end pattern continues beyond the current
2582 * position.
2583 */
2584 if (cur_si->si_end_idx
2585 && (cur_si->si_eoe_pos.lnum > current_lnum
2586 || (cur_si->si_eoe_pos.lnum == current_lnum
2587 && cur_si->si_eoe_pos.col > current_col)))
2588 {
2589 cur_si->si_idx = cur_si->si_end_idx;
2590 cur_si->si_end_idx = 0;
2591 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2592 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2593 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002594#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002595 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002596 if (cur_si->si_flags & HL_CONCEALENDS)
2597 cur_si->si_flags |= HL_CONCEAL;
2598#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002599 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002600
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002601 /* nextgroup= should not match in the end pattern */
2602 current_next_list = NULL;
2603
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002604 /* what matches next may be different now, clear it */
2605 next_match_idx = 0;
2606 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002607 break;
2608 }
2609 else
2610 {
2611 /* handle next_list, unless at end of line and no "skipnl" or
2612 * "skipempty" */
2613 current_next_list = cur_si->si_next_list;
2614 current_next_flags = cur_si->si_flags;
2615 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2616 && syn_getcurline()[current_col] == NUL)
2617 current_next_list = NULL;
2618
2619 /* When the ended item has "extend", another item with
2620 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002621 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002622
2623 pop_current_state();
2624
2625 if (current_state.ga_len == 0)
2626 break;
2627
Bram Moolenaar81993f42008-01-11 20:27:45 +00002628 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002629 {
2630 syn_update_ends(FALSE);
2631 if (current_state.ga_len == 0)
2632 break;
2633 }
2634
2635 cur_si = &CUR_STATE(current_state.ga_len - 1);
2636
2637 /*
2638 * Only for a region the search for the end continues after
2639 * the end of the contained item. If the contained match
2640 * included the end-of-line, break here, the region continues.
2641 * Don't do this when:
2642 * - "keepend" is used for the contained item
2643 * - not at the end of the line (could be end="x$"me=e-1).
2644 * - "excludenl" is used (HL_HAS_EOL won't be set)
2645 */
2646 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002647 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002648 == SPTYPE_START
2649 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2650 {
2651 update_si_end(cur_si, (int)current_col, TRUE);
2652 check_keepend();
2653 if ((current_next_flags & HL_HAS_EOL)
2654 && keepend_level < 0
2655 && syn_getcurline()[current_col] == NUL)
2656 break;
2657 }
2658 }
2659 }
2660 else
2661 break;
2662 }
2663}
2664
2665/*
2666 * Update an entry in the current_state stack for a match or region. This
2667 * fills in si_attr, si_next_list and si_cont_list.
2668 */
2669 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002670update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002671{
2672 stateitem_T *sip = &CUR_STATE(idx);
2673 synpat_T *spp;
2674
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002675 /* This should not happen... */
2676 if (sip->si_idx < 0)
2677 return;
2678
Bram Moolenaar860cae12010-06-05 23:22:07 +02002679 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002680 if (sip->si_flags & HL_MATCH)
2681 sip->si_id = spp->sp_syn_match_id;
2682 else
2683 sip->si_id = spp->sp_syn.id;
2684 sip->si_attr = syn_id2attr(sip->si_id);
2685 sip->si_trans_id = sip->si_id;
2686 if (sip->si_flags & HL_MATCH)
2687 sip->si_cont_list = NULL;
2688 else
2689 sip->si_cont_list = spp->sp_cont_list;
2690
2691 /*
2692 * For transparent items, take attr from outer item.
2693 * Also take cont_list, if there is none.
2694 * Don't do this for the matchgroup of a start or end pattern.
2695 */
2696 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2697 {
2698 if (idx == 0)
2699 {
2700 sip->si_attr = 0;
2701 sip->si_trans_id = 0;
2702 if (sip->si_cont_list == NULL)
2703 sip->si_cont_list = ID_LIST_ALL;
2704 }
2705 else
2706 {
2707 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2708 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002709 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2710 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002711 if (sip->si_cont_list == NULL)
2712 {
2713 sip->si_flags |= HL_TRANS_CONT;
2714 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2715 }
2716 }
2717 }
2718}
2719
2720/*
2721 * Check the current stack for patterns with "keepend" flag.
2722 * Propagate the match-end to contained items, until a "skipend" item is found.
2723 */
2724 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002725check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002726{
2727 int i;
2728 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002729 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002730 stateitem_T *sip;
2731
2732 /*
2733 * This check can consume a lot of time; only do it from the level where
2734 * there really is a keepend.
2735 */
2736 if (keepend_level < 0)
2737 return;
2738
2739 /*
2740 * Find the last index of an "extend" item. "keepend" items before that
2741 * won't do anything. If there is no "extend" item "i" will be
2742 * "keepend_level" and all "keepend" items will work normally.
2743 */
2744 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2745 if (CUR_STATE(i).si_flags & HL_EXTEND)
2746 break;
2747
2748 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002749 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002750 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002751 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002752 for ( ; i < current_state.ga_len; ++i)
2753 {
2754 sip = &CUR_STATE(i);
2755 if (maxpos.lnum != 0)
2756 {
2757 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002758 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002759 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2760 sip->si_ends = TRUE;
2761 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002762 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2763 {
2764 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002765 || maxpos.lnum > sip->si_m_endpos.lnum
2766 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002767 && maxpos.col > sip->si_m_endpos.col))
2768 maxpos = sip->si_m_endpos;
2769 if (maxpos_h.lnum == 0
2770 || maxpos_h.lnum > sip->si_h_endpos.lnum
2771 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2772 && maxpos_h.col > sip->si_h_endpos.col))
2773 maxpos_h = sip->si_h_endpos;
2774 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002775 }
2776}
2777
2778/*
2779 * Update an entry in the current_state stack for a start-skip-end pattern.
2780 * This finds the end of the current item, if it's in the current line.
2781 *
2782 * Return the flags for the matched END.
2783 */
2784 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002785update_si_end(
2786 stateitem_T *sip,
2787 int startcol, /* where to start searching for the end */
2788 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002789{
2790 lpos_T startpos;
2791 lpos_T endpos;
2792 lpos_T hl_endpos;
2793 lpos_T end_endpos;
2794 int end_idx;
2795
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002796 /* return quickly for a keyword */
2797 if (sip->si_idx < 0)
2798 return;
2799
Bram Moolenaar071d4272004-06-13 20:20:40 +00002800 /* Don't update when it's already done. Can be a match of an end pattern
2801 * that started in a previous line. Watch out: can also be a "keepend"
2802 * from a containing item. */
2803 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2804 return;
2805
2806 /*
2807 * We need to find the end of the region. It may continue in the next
2808 * line.
2809 */
2810 end_idx = 0;
2811 startpos.lnum = current_lnum;
2812 startpos.col = startcol;
2813 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2814 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2815
2816 if (endpos.lnum == 0)
2817 {
2818 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002819 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002820 {
2821 /* a "oneline" never continues in the next line */
2822 sip->si_ends = TRUE;
2823 sip->si_m_endpos.lnum = current_lnum;
2824 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2825 }
2826 else
2827 {
2828 /* continues in the next line */
2829 sip->si_ends = FALSE;
2830 sip->si_m_endpos.lnum = 0;
2831 }
2832 sip->si_h_endpos = sip->si_m_endpos;
2833 }
2834 else
2835 {
2836 /* match within this line */
2837 sip->si_m_endpos = endpos;
2838 sip->si_h_endpos = hl_endpos;
2839 sip->si_eoe_pos = end_endpos;
2840 sip->si_ends = TRUE;
2841 sip->si_end_idx = end_idx;
2842 }
2843}
2844
2845/*
2846 * Add a new state to the current state stack.
2847 * It is cleared and the index set to "idx".
2848 * Return FAIL if it's not possible (out of memory).
2849 */
2850 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002851push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002852{
2853 if (ga_grow(&current_state, 1) == FAIL)
2854 return FAIL;
2855 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2856 CUR_STATE(current_state.ga_len).si_idx = idx;
2857 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002858 return OK;
2859}
2860
2861/*
2862 * Remove a state from the current_state stack.
2863 */
2864 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002865pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002866{
2867 if (current_state.ga_len)
2868 {
2869 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2870 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002871 }
2872 /* after the end of a pattern, try matching a keyword or pattern */
2873 next_match_idx = -1;
2874
2875 /* if first state with "keepend" is popped, reset keepend_level */
2876 if (keepend_level >= current_state.ga_len)
2877 keepend_level = -1;
2878}
2879
2880/*
2881 * Find the end of a start/skip/end syntax region after "startpos".
2882 * Only checks one line.
2883 * Also handles a match item that continued from a previous line.
2884 * If not found, the syntax item continues in the next line. m_endpos->lnum
2885 * will be 0.
2886 * If found, the end of the region and the end of the highlighting is
2887 * computed.
2888 */
2889 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002890find_endpos(
2891 int idx, /* index of the pattern */
2892 lpos_T *startpos, /* where to start looking for an END match */
2893 lpos_T *m_endpos, /* return: end of match */
2894 lpos_T *hl_endpos, /* return: end of highlighting */
2895 long *flagsp, /* return: flags of matching END */
2896 lpos_T *end_endpos, /* return: end of end pattern match */
2897 int *end_idx, /* return: group ID for end pat. match, or 0 */
2898 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002899{
2900 colnr_T matchcol;
2901 synpat_T *spp, *spp_skip;
2902 int start_idx;
2903 int best_idx;
2904 regmmatch_T regmatch;
2905 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2906 lpos_T pos;
2907 char_u *line;
2908 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002909 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002910
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002911 /* just in case we are invoked for a keyword */
2912 if (idx < 0)
2913 return;
2914
Bram Moolenaar071d4272004-06-13 20:20:40 +00002915 /*
2916 * Check for being called with a START pattern.
2917 * Can happen with a match that continues to the next line, because it
2918 * contained a region.
2919 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002920 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002921 if (spp->sp_type != SPTYPE_START)
2922 {
2923 *hl_endpos = *startpos;
2924 return;
2925 }
2926
2927 /*
2928 * Find the SKIP or first END pattern after the last START pattern.
2929 */
2930 for (;;)
2931 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002932 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002933 if (spp->sp_type != SPTYPE_START)
2934 break;
2935 ++idx;
2936 }
2937
2938 /*
2939 * Lookup the SKIP pattern (if present)
2940 */
2941 if (spp->sp_type == SPTYPE_SKIP)
2942 {
2943 spp_skip = spp;
2944 ++idx;
2945 }
2946 else
2947 spp_skip = NULL;
2948
2949 /* Setup external matches for syn_regexec(). */
2950 unref_extmatch(re_extmatch_in);
2951 re_extmatch_in = ref_extmatch(start_ext);
2952
2953 matchcol = startpos->col; /* start looking for a match at sstart */
2954 start_idx = idx; /* remember the first END pattern. */
2955 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002956
2957 /* use syntax iskeyword option */
2958 save_chartab(buf_chartab);
2959
Bram Moolenaar071d4272004-06-13 20:20:40 +00002960 for (;;)
2961 {
2962 /*
2963 * Find end pattern that matches first after "matchcol".
2964 */
2965 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002966 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002967 {
2968 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002969 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002970
Bram Moolenaar860cae12010-06-05 23:22:07 +02002971 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002972 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2973 break;
2974 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2975 if (lc_col < 0)
2976 lc_col = 0;
2977
2978 regmatch.rmm_ic = spp->sp_ic;
2979 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002980 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
2981 IF_SYN_TIME(&spp->sp_time));
2982 spp->sp_prog = regmatch.regprog;
2983 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002984 {
2985 if (best_idx == -1 || regmatch.startpos[0].col
2986 < best_regmatch.startpos[0].col)
2987 {
2988 best_idx = idx;
2989 best_regmatch.startpos[0] = regmatch.startpos[0];
2990 best_regmatch.endpos[0] = regmatch.endpos[0];
2991 }
2992 }
2993 }
2994
2995 /*
2996 * If all end patterns have been tried, and there is no match, the
2997 * item continues until end-of-line.
2998 */
2999 if (best_idx == -1)
3000 break;
3001
3002 /*
3003 * If the skip pattern matches before the end pattern,
3004 * continue searching after the skip pattern.
3005 */
3006 if (spp_skip != NULL)
3007 {
3008 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003009 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003010
3011 if (lc_col < 0)
3012 lc_col = 0;
3013 regmatch.rmm_ic = spp_skip->sp_ic;
3014 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003015 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3016 IF_SYN_TIME(&spp_skip->sp_time));
3017 spp_skip->sp_prog = regmatch.regprog;
3018 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003019 <= best_regmatch.startpos[0].col)
3020 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003021 int line_len;
3022
Bram Moolenaar071d4272004-06-13 20:20:40 +00003023 /* Add offset to skip pattern match */
3024 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3025
3026 /* If the skip pattern goes on to the next line, there is no
3027 * match with an end pattern in this line. */
3028 if (pos.lnum > startpos->lnum)
3029 break;
3030
3031 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003032 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003033
3034 /* take care of an empty match or negative offset */
3035 if (pos.col <= matchcol)
3036 ++matchcol;
3037 else if (pos.col <= regmatch.endpos[0].col)
3038 matchcol = pos.col;
3039 else
3040 /* Be careful not to jump over the NUL at the end-of-line */
3041 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003042 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003043 ++matchcol)
3044 ;
3045
3046 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003047 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003048 break;
3049
3050 continue; /* start with first end pattern again */
3051 }
3052 }
3053
3054 /*
3055 * Match from start pattern to end pattern.
3056 * Correct for match and highlight offset of end pattern.
3057 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003058 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003059 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3060 /* can't end before the start */
3061 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3062 m_endpos->col = startpos->col;
3063
3064 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3065 /* can't end before the start */
3066 if (end_endpos->lnum == startpos->lnum
3067 && end_endpos->col < startpos->col)
3068 end_endpos->col = startpos->col;
3069 /* can't end after the match */
3070 limit_pos(end_endpos, m_endpos);
3071
3072 /*
3073 * If the end group is highlighted differently, adjust the pointers.
3074 */
3075 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3076 {
3077 *end_idx = best_idx;
3078 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3079 {
3080 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3081 hl_endpos->col = best_regmatch.endpos[0].col;
3082 }
3083 else
3084 {
3085 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3086 hl_endpos->col = best_regmatch.startpos[0].col;
3087 }
3088 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3089
3090 /* can't end before the start */
3091 if (hl_endpos->lnum == startpos->lnum
3092 && hl_endpos->col < startpos->col)
3093 hl_endpos->col = startpos->col;
3094 limit_pos(hl_endpos, m_endpos);
3095
3096 /* now the match ends where the highlighting ends, it is turned
3097 * into the matchgroup for the end */
3098 *m_endpos = *hl_endpos;
3099 }
3100 else
3101 {
3102 *end_idx = 0;
3103 *hl_endpos = *end_endpos;
3104 }
3105
3106 *flagsp = spp->sp_flags;
3107
3108 had_match = TRUE;
3109 break;
3110 }
3111
3112 /* no match for an END pattern in this line */
3113 if (!had_match)
3114 m_endpos->lnum = 0;
3115
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003116 restore_chartab(buf_chartab);
3117
Bram Moolenaar071d4272004-06-13 20:20:40 +00003118 /* Remove external matches. */
3119 unref_extmatch(re_extmatch_in);
3120 re_extmatch_in = NULL;
3121}
3122
3123/*
3124 * Limit "pos" not to be after "limit".
3125 */
3126 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003127limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003128{
3129 if (pos->lnum > limit->lnum)
3130 *pos = *limit;
3131 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3132 pos->col = limit->col;
3133}
3134
3135/*
3136 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3137 */
3138 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003139limit_pos_zero(
3140 lpos_T *pos,
3141 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003142{
3143 if (pos->lnum == 0)
3144 *pos = *limit;
3145 else
3146 limit_pos(pos, limit);
3147}
3148
3149/*
3150 * Add offset to matched text for end of match or highlight.
3151 */
3152 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003153syn_add_end_off(
3154 lpos_T *result, /* returned position */
3155 regmmatch_T *regmatch, /* start/end of match */
3156 synpat_T *spp, /* matched pattern */
3157 int idx, /* index of offset */
3158 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003159{
3160 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003161 int off;
3162 char_u *base;
3163 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003164
3165 if (spp->sp_off_flags & (1 << idx))
3166 {
3167 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003168 col = regmatch->startpos[0].col;
3169 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003170 }
3171 else
3172 {
3173 result->lnum = regmatch->endpos[0].lnum;
3174 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003175 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003176 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003177 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3178 * is a matchgroup. Watch out for match with last NL in the buffer. */
3179 if (result->lnum > syn_buf->b_ml.ml_line_count)
3180 col = 0;
3181 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003182 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003183 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3184 p = base + col;
3185 if (off > 0)
3186 {
3187 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003188 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003189 }
3190 else if (off < 0)
3191 {
3192 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003193 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003194 }
3195 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003196 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003197 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003198}
3199
3200/*
3201 * Add offset to matched text for start of match or highlight.
3202 * Avoid resulting column to become negative.
3203 */
3204 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003205syn_add_start_off(
3206 lpos_T *result, /* returned position */
3207 regmmatch_T *regmatch, /* start/end of match */
3208 synpat_T *spp,
3209 int idx,
3210 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003211{
3212 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003213 int off;
3214 char_u *base;
3215 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003216
3217 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3218 {
3219 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003220 col = regmatch->endpos[0].col;
3221 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003222 }
3223 else
3224 {
3225 result->lnum = regmatch->startpos[0].lnum;
3226 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003227 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003228 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003229 if (result->lnum > syn_buf->b_ml.ml_line_count)
3230 {
3231 /* a "\n" at the end of the pattern may take us below the last line */
3232 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003233 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003234 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003235 if (off != 0)
3236 {
3237 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3238 p = base + col;
3239 if (off > 0)
3240 {
3241 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003242 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003243 }
3244 else if (off < 0)
3245 {
3246 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003247 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003248 }
3249 col = (int)(p - base);
3250 }
3251 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003252}
3253
3254/*
3255 * Get current line in syntax buffer.
3256 */
3257 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003258syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003259{
3260 return ml_get_buf(syn_buf, current_lnum, FALSE);
3261}
3262
3263/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003264 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003265 * Returns TRUE when there is a match.
3266 */
3267 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003268syn_regexec(
3269 regmmatch_T *rmp,
3270 linenr_T lnum,
3271 colnr_T col,
3272 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003273{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003274 int r;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003275#ifdef FEAT_RELTIME
3276 int timed_out = FALSE;
3277#endif
Bram Moolenaarf7512552013-06-06 14:55:19 +02003278#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003279 proftime_T pt;
3280
3281 if (syn_time_on)
3282 profile_start(&pt);
3283#endif
3284
Bram Moolenaarbcf94422018-06-23 14:21:42 +02003285 if (rmp->regprog == NULL)
3286 // This can happen if a previous call to vim_regexec_multi() tried to
3287 // use the NFA engine, which resulted in NFA_TOO_EXPENSIVE, and
3288 // compiling the pattern with the other engine fails.
3289 return FALSE;
3290
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003291 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003292 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
3293#ifdef FEAT_RELTIME
3294 syn_tm, &timed_out
3295#else
3296 NULL, NULL
3297#endif
3298 );
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003299
Bram Moolenaarf7512552013-06-06 14:55:19 +02003300#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003301 if (syn_time_on)
3302 {
3303 profile_end(&pt);
3304 profile_add(&st->total, &pt);
3305 if (profile_cmp(&pt, &st->slowest) < 0)
3306 st->slowest = pt;
3307 ++st->count;
3308 if (r > 0)
3309 ++st->match;
3310 }
3311#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003312#ifdef FEAT_RELTIME
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003313 if (timed_out && !syn_win->w_s->b_syn_slow)
3314 {
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003315 syn_win->w_s->b_syn_slow = TRUE;
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003316 MSG(_("'redrawtime' exceeded, syntax highlighting disabled"));
3317 }
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003318#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003319
3320 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003321 {
3322 rmp->startpos[0].lnum += lnum;
3323 rmp->endpos[0].lnum += lnum;
3324 return TRUE;
3325 }
3326 return FALSE;
3327}
3328
3329/*
3330 * Check one position in a line for a matching keyword.
3331 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003332 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003333 */
3334 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003335check_keyword_id(
3336 char_u *line,
3337 int startcol, /* position in line to check for keyword */
3338 int *endcolp, /* return: character after found keyword */
3339 long *flagsp, /* return: flags of matching keyword */
3340 short **next_listp, /* return: next_list of matching keyword */
3341 stateitem_T *cur_si, /* item at the top of the stack */
3342 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003343{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003344 keyentry_T *kp;
3345 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003346 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003347 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003348 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003349 hashtab_T *ht;
3350 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003351
3352 /* Find first character after the keyword. First character was already
3353 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003354 kwp = line + startcol;
3355 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003356 do
3357 {
3358#ifdef FEAT_MBYTE
3359 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003360 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003361 else
3362#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003363 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003364 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003365 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003366
Bram Moolenaardad6b692005-01-25 22:14:34 +00003367 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003368 return 0;
3369
3370 /*
3371 * Must make a copy of the keyword, so we can add a NUL and make it
3372 * lowercase.
3373 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003374 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003375
3376 /*
3377 * Try twice:
3378 * 1. matching case
3379 * 2. ignoring case
3380 */
3381 for (round = 1; round <= 2; ++round)
3382 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003383 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003384 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003385 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003386 if (round == 2) /* ignore case */
3387 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003388
3389 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003390 * Find keywords that match. There can be several with different
3391 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003392 * When current_next_list is non-zero accept only that group, otherwise:
3393 * Accept a not-contained keyword at toplevel.
3394 * Accept a keyword at other levels only if it is in the contains list.
3395 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003396 hi = hash_find(ht, keyword);
3397 if (!HASHITEM_EMPTY(hi))
3398 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003399 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003400 if (current_next_list != 0
3401 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3402 : (cur_si == NULL
3403 ? !(kp->flags & HL_CONTAINED)
3404 : in_id_list(cur_si, cur_si->si_cont_list,
3405 &kp->k_syn, kp->flags & HL_CONTAINED)))
3406 {
3407 *endcolp = startcol + kwlen;
3408 *flagsp = kp->flags;
3409 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003410#ifdef FEAT_CONCEAL
3411 *ccharp = kp->k_char;
3412#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003413 return kp->k_syn.id;
3414 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003415 }
3416 }
3417 return 0;
3418}
3419
3420/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003421 * Handle ":syntax conceal" command.
3422 */
3423 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003424syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003425{
3426#ifdef FEAT_CONCEAL
3427 char_u *arg = eap->arg;
3428 char_u *next;
3429
3430 eap->nextcmd = find_nextcmd(arg);
3431 if (eap->skip)
3432 return;
3433
3434 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003435 if (*arg == NUL)
3436 {
3437 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar83064062017-06-13 16:34:54 +02003438 MSG(_("syntax conceal on"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003439 else
Bram Moolenaar83064062017-06-13 16:34:54 +02003440 MSG(_("syntax conceal off"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003441 }
3442 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003443 curwin->w_s->b_syn_conceal = TRUE;
3444 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3445 curwin->w_s->b_syn_conceal = FALSE;
3446 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003447 semsg(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003448#endif
3449}
3450
3451/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003452 * Handle ":syntax case" command.
3453 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003454 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003455syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003456{
3457 char_u *arg = eap->arg;
3458 char_u *next;
3459
3460 eap->nextcmd = find_nextcmd(arg);
3461 if (eap->skip)
3462 return;
3463
3464 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003465 if (*arg == NUL)
3466 {
3467 if (curwin->w_s->b_syn_ic)
3468 MSG(_("syntax case ignore"));
3469 else
3470 MSG(_("syntax case match"));
3471 }
3472 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003473 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003474 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003475 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003476 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003477 semsg(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003478}
3479
3480/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003481 * Handle ":syntax spell" command.
3482 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003483 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003484syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003485{
3486 char_u *arg = eap->arg;
3487 char_u *next;
3488
3489 eap->nextcmd = find_nextcmd(arg);
3490 if (eap->skip)
3491 return;
3492
3493 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003494 if (*arg == NUL)
3495 {
3496 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
3497 MSG(_("syntax spell toplevel"));
3498 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
3499 MSG(_("syntax spell notoplevel"));
3500 else
3501 MSG(_("syntax spell default"));
3502 }
3503 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003504 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003505 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003506 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003507 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003508 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003509 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003510 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003511 semsg(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003512 return;
3513 }
3514
3515 /* assume spell checking changed, force a redraw */
3516 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003517}
3518
3519/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003520 * Handle ":syntax iskeyword" command.
3521 */
3522 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003523syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003524{
3525 char_u *arg = eap->arg;
3526 char_u save_chartab[32];
3527 char_u *save_isk;
3528
3529 if (eap->skip)
3530 return;
3531
3532 arg = skipwhite(arg);
3533 if (*arg == NUL)
3534 {
3535 MSG_PUTS("\n");
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003536 if (curwin->w_s->b_syn_isk != empty_option)
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003537 {
3538 MSG_PUTS(_("syntax iskeyword "));
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003539 msg_outtrans(curwin->w_s->b_syn_isk);
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003540 }
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003541 else
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003542 msg_outtrans((char_u *)_("syntax iskeyword not set"));
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003543 }
3544 else
3545 {
3546 if (STRNICMP(arg, "clear", 5) == 0)
3547 {
3548 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3549 (size_t)32);
3550 clear_string_option(&curwin->w_s->b_syn_isk);
3551 }
3552 else
3553 {
3554 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3555 save_isk = curbuf->b_p_isk;
3556 curbuf->b_p_isk = vim_strsave(arg);
3557
3558 buf_init_chartab(curbuf, FALSE);
3559 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3560 (size_t)32);
3561 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3562 clear_string_option(&curwin->w_s->b_syn_isk);
3563 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3564 curbuf->b_p_isk = save_isk;
3565 }
3566 }
3567 redraw_win_later(curwin, NOT_VALID);
3568}
3569
3570/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003571 * Clear all syntax info for one buffer.
3572 */
3573 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003574syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003575{
3576 int i;
3577
Bram Moolenaar860cae12010-06-05 23:22:07 +02003578 block->b_syn_error = FALSE; /* clear previous error */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003579#ifdef FEAT_RELTIME
3580 block->b_syn_slow = FALSE; /* clear previous timeout */
3581#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003582 block->b_syn_ic = FALSE; /* Use case, by default */
3583 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3584 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003585#ifdef FEAT_CONCEAL
3586 block->b_syn_conceal = FALSE;
3587#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003588
3589 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003590 clear_keywtab(&block->b_keywtab);
3591 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003592
3593 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003594 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3595 syn_clear_pattern(block, i);
3596 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003597
3598 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003599 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3600 syn_clear_cluster(block, i);
3601 ga_clear(&block->b_syn_clusters);
3602 block->b_spell_cluster_id = 0;
3603 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003604
Bram Moolenaar860cae12010-06-05 23:22:07 +02003605 block->b_syn_sync_flags = 0;
3606 block->b_syn_sync_minlines = 0;
3607 block->b_syn_sync_maxlines = 0;
3608 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003609
Bram Moolenaar473de612013-06-08 18:19:48 +02003610 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003611 block->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003612 VIM_CLEAR(block->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003613#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003614 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003615#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003616 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003617
3618 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003619 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003620 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003621
3622 /* Reset the counter for ":syn include" */
3623 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003624}
3625
3626/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003627 * Get rid of ownsyntax for window "wp".
3628 */
3629 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003630reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003631{
3632 if (wp->w_s != &wp->w_buffer->b_s)
3633 {
3634 syntax_clear(wp->w_s);
3635 vim_free(wp->w_s);
3636 wp->w_s = &wp->w_buffer->b_s;
3637 }
3638}
3639
3640/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003641 * Clear syncing info for one buffer.
3642 */
3643 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003644syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003645{
3646 int i;
3647
3648 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003649 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3650 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3651 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003652
Bram Moolenaar860cae12010-06-05 23:22:07 +02003653 curwin->w_s->b_syn_sync_flags = 0;
3654 curwin->w_s->b_syn_sync_minlines = 0;
3655 curwin->w_s->b_syn_sync_maxlines = 0;
3656 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003657
Bram Moolenaar473de612013-06-08 18:19:48 +02003658 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003659 curwin->w_s->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003660 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003661 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003662
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003663 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003664}
3665
3666/*
3667 * Remove one pattern from the buffer's pattern list.
3668 */
3669 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003670syn_remove_pattern(
3671 synblock_T *block,
3672 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003673{
3674 synpat_T *spp;
3675
Bram Moolenaar860cae12010-06-05 23:22:07 +02003676 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003677#ifdef FEAT_FOLDING
3678 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003679 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003680#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003681 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003682 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003683 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3684 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003685}
3686
3687/*
3688 * Clear and free one syntax pattern. When clearing all, must be called from
3689 * last to first!
3690 */
3691 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003692syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003693{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003694 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003695 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003696 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003697 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003698 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003699 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3700 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3701 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003702 }
3703}
3704
3705/*
3706 * Clear and free one syntax cluster.
3707 */
3708 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003709syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003710{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003711 vim_free(SYN_CLSTR(block)[i].scl_name);
3712 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3713 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003714}
3715
3716/*
3717 * Handle ":syntax clear" command.
3718 */
3719 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003720syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003721{
3722 char_u *arg = eap->arg;
3723 char_u *arg_end;
3724 int id;
3725
3726 eap->nextcmd = find_nextcmd(arg);
3727 if (eap->skip)
3728 return;
3729
3730 /*
3731 * We have to disable this within ":syn include @group filename",
3732 * because otherwise @group would get deleted.
3733 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3734 * clear".
3735 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003736 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003737 return;
3738
3739 if (ends_excmd(*arg))
3740 {
3741 /*
3742 * No argument: Clear all syntax items.
3743 */
3744 if (syncing)
3745 syntax_sync_clear();
3746 else
3747 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003748 syntax_clear(curwin->w_s);
3749 if (curwin->w_s == &curwin->w_buffer->b_s)
3750 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003751 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003752 }
3753 }
3754 else
3755 {
3756 /*
3757 * Clear the group IDs that are in the argument.
3758 */
3759 while (!ends_excmd(*arg))
3760 {
3761 arg_end = skiptowhite(arg);
3762 if (*arg == '@')
3763 {
3764 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3765 if (id == 0)
3766 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003767 semsg(_("E391: No such syntax cluster: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003768 break;
3769 }
3770 else
3771 {
3772 /*
3773 * We can't physically delete a cluster without changing
3774 * the IDs of other clusters, so we do the next best thing
3775 * and make it empty.
3776 */
3777 short scl_id = id - SYNID_CLUSTER;
3778
Bram Moolenaard23a8232018-02-10 18:45:26 +01003779 VIM_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003780 }
3781 }
3782 else
3783 {
3784 id = syn_namen2id(arg, (int)(arg_end - arg));
3785 if (id == 0)
3786 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003787 semsg(_(e_nogroup), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003788 break;
3789 }
3790 else
3791 syn_clear_one(id, syncing);
3792 }
3793 arg = skipwhite(arg_end);
3794 }
3795 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003796 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003797 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003798}
3799
3800/*
3801 * Clear one syntax group for the current buffer.
3802 */
3803 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003804syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003805{
3806 synpat_T *spp;
3807 int idx;
3808
3809 /* Clear keywords only when not ":syn sync clear group-name" */
3810 if (!syncing)
3811 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003812 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3813 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003814 }
3815
3816 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003817 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003818 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003819 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003820 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3821 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003822 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003823 }
3824}
3825
3826/*
3827 * Handle ":syntax on" command.
3828 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003829 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003830syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003831{
3832 syn_cmd_onoff(eap, "syntax");
3833}
3834
3835/*
3836 * Handle ":syntax enable" command.
3837 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003838 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003839syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003840{
3841 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3842 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003843 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003844}
3845
3846/*
3847 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003848 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003849 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003850 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003851syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003852{
3853 eap->nextcmd = check_nextcmd(eap->arg);
3854 if (!eap->skip)
3855 {
3856 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3857 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003858 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003859 }
3860}
3861
3862/*
3863 * Handle ":syntax manual" command.
3864 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003865 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003866syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003867{
3868 syn_cmd_onoff(eap, "manual");
3869}
3870
3871/*
3872 * Handle ":syntax off" command.
3873 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003874 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003875syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003876{
3877 syn_cmd_onoff(eap, "nosyntax");
3878}
3879
3880 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003881syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003882{
3883 char_u buf[100];
3884
3885 eap->nextcmd = check_nextcmd(eap->arg);
3886 if (!eap->skip)
3887 {
3888 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003889 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003890 do_cmdline_cmd(buf);
3891 }
3892}
3893
3894/*
3895 * Handle ":syntax [list]" command: list current syntax words.
3896 */
3897 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003898syn_cmd_list(
3899 exarg_T *eap,
3900 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003901{
3902 char_u *arg = eap->arg;
3903 int id;
3904 char_u *arg_end;
3905
3906 eap->nextcmd = find_nextcmd(arg);
3907 if (eap->skip)
3908 return;
3909
Bram Moolenaar860cae12010-06-05 23:22:07 +02003910 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003911 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003912 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003913 return;
3914 }
3915
3916 if (syncing)
3917 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003918 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003919 {
3920 MSG_PUTS(_("syncing on C-style comments"));
3921 syn_lines_msg();
3922 syn_match_msg();
3923 return;
3924 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003925 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003926 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003927 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003928 MSG_PUTS(_("no syncing"));
3929 else
3930 {
3931 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003932 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003933 MSG_PUTS(_(" lines before top line"));
3934 syn_match_msg();
3935 }
3936 return;
3937 }
3938 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003939 if (curwin->w_s->b_syn_sync_minlines > 0
3940 || curwin->w_s->b_syn_sync_maxlines > 0
3941 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003942 {
3943 MSG_PUTS(_("\nsyncing on items"));
3944 syn_lines_msg();
3945 syn_match_msg();
3946 }
3947 }
3948 else
3949 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3950 if (ends_excmd(*arg))
3951 {
3952 /*
3953 * No argument: List all group IDs and all syntax clusters.
3954 */
3955 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3956 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003957 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003958 syn_list_cluster(id);
3959 }
3960 else
3961 {
3962 /*
3963 * List the group IDs and syntax clusters that are in the argument.
3964 */
3965 while (!ends_excmd(*arg) && !got_int)
3966 {
3967 arg_end = skiptowhite(arg);
3968 if (*arg == '@')
3969 {
3970 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3971 if (id == 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003972 semsg(_("E392: No such syntax cluster: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003973 else
3974 syn_list_cluster(id - SYNID_CLUSTER);
3975 }
3976 else
3977 {
3978 id = syn_namen2id(arg, (int)(arg_end - arg));
3979 if (id == 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003980 semsg(_(e_nogroup), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003981 else
3982 syn_list_one(id, syncing, TRUE);
3983 }
3984 arg = skipwhite(arg_end);
3985 }
3986 }
3987 eap->nextcmd = check_nextcmd(arg);
3988}
3989
3990 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003991syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003992{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003993 if (curwin->w_s->b_syn_sync_maxlines > 0
3994 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003995 {
3996 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003997 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003998 {
3999 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004000 msg_outnum(curwin->w_s->b_syn_sync_minlines);
4001 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004002 MSG_PUTS(", ");
4003 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004004 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004005 {
4006 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004007 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004008 }
4009 MSG_PUTS(_(" lines before top line"));
4010 }
4011}
4012
4013 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004014syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004015{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004016 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004017 {
4018 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004019 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004020 MSG_PUTS(_(" line breaks"));
4021 }
4022}
4023
4024static int last_matchgroup;
4025
4026struct name_list
4027{
4028 int flag;
4029 char *name;
4030};
4031
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004032static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004033
4034/*
4035 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4036 */
4037 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004038syn_list_one(
4039 int id,
4040 int syncing, /* when TRUE: list syncing items */
4041 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004042{
4043 int attr;
4044 int idx;
4045 int did_header = FALSE;
4046 synpat_T *spp;
4047 static struct name_list namelist1[] =
4048 {
4049 {HL_DISPLAY, "display"},
4050 {HL_CONTAINED, "contained"},
4051 {HL_ONELINE, "oneline"},
4052 {HL_KEEPEND, "keepend"},
4053 {HL_EXTEND, "extend"},
4054 {HL_EXCLUDENL, "excludenl"},
4055 {HL_TRANSP, "transparent"},
4056 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004057#ifdef FEAT_CONCEAL
4058 {HL_CONCEAL, "conceal"},
4059 {HL_CONCEALENDS, "concealends"},
4060#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004061 {0, NULL}
4062 };
4063 static struct name_list namelist2[] =
4064 {
4065 {HL_SKIPWHITE, "skipwhite"},
4066 {HL_SKIPNL, "skipnl"},
4067 {HL_SKIPEMPTY, "skipempty"},
4068 {0, NULL}
4069 };
4070
Bram Moolenaar8820b482017-03-16 17:23:31 +01004071 attr = HL_ATTR(HLF_D); /* highlight like directories */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004072
4073 /* list the keywords for "id" */
4074 if (!syncing)
4075 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004076 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4077 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004078 did_header, attr);
4079 }
4080
4081 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004082 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004083 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004084 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004085 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4086 continue;
4087
4088 (void)syn_list_header(did_header, 999, id);
4089 did_header = TRUE;
4090 last_matchgroup = 0;
4091 if (spp->sp_type == SPTYPE_MATCH)
4092 {
4093 put_pattern("match", ' ', spp, attr);
4094 msg_putchar(' ');
4095 }
4096 else if (spp->sp_type == SPTYPE_START)
4097 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004098 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4099 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4100 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4101 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4102 while (idx < curwin->w_s->b_syn_patterns.ga_len
4103 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4104 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004105 --idx;
4106 msg_putchar(' ');
4107 }
4108 syn_list_flags(namelist1, spp->sp_flags, attr);
4109
4110 if (spp->sp_cont_list != NULL)
4111 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4112
4113 if (spp->sp_syn.cont_in_list != NULL)
4114 put_id_list((char_u *)"containedin",
4115 spp->sp_syn.cont_in_list, attr);
4116
4117 if (spp->sp_next_list != NULL)
4118 {
4119 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4120 syn_list_flags(namelist2, spp->sp_flags, attr);
4121 }
4122 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4123 {
4124 if (spp->sp_flags & HL_SYNC_HERE)
4125 msg_puts_attr((char_u *)"grouphere", attr);
4126 else
4127 msg_puts_attr((char_u *)"groupthere", attr);
4128 msg_putchar(' ');
4129 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004130 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004131 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4132 else
4133 MSG_PUTS("NONE");
4134 msg_putchar(' ');
4135 }
4136 }
4137
4138 /* list the link, if there is one */
4139 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4140 {
4141 (void)syn_list_header(did_header, 999, id);
4142 msg_puts_attr((char_u *)"links to", attr);
4143 msg_putchar(' ');
4144 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4145 }
4146}
4147
4148 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004149syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004150{
4151 int i;
4152
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004153 for (i = 0; nlist[i].flag != 0; ++i)
4154 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004155 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004156 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004157 msg_putchar(' ');
4158 }
4159}
4160
4161/*
4162 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4163 */
4164 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004165syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004166{
4167 int endcol = 15;
4168
4169 /* slight hack: roughly duplicate the guts of syn_list_header() */
4170 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004171 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004172
4173 if (msg_col >= endcol) /* output at least one space */
4174 endcol = msg_col + 1;
4175 if (Columns <= endcol) /* avoid hang for tiny window */
4176 endcol = Columns - 1;
4177
4178 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004179 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004180 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004181 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004182 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004183 }
4184 else
4185 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01004186 msg_puts_attr((char_u *)"cluster", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004187 msg_puts((char_u *)"=NONE");
4188 }
4189}
4190
4191 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004192put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004193{
4194 short *p;
4195
4196 msg_puts_attr(name, attr);
4197 msg_putchar('=');
4198 for (p = list; *p; ++p)
4199 {
4200 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4201 {
4202 if (p[1])
4203 MSG_PUTS("ALLBUT");
4204 else
4205 MSG_PUTS("ALL");
4206 }
4207 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4208 {
4209 MSG_PUTS("TOP");
4210 }
4211 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4212 {
4213 MSG_PUTS("CONTAINED");
4214 }
4215 else if (*p >= SYNID_CLUSTER)
4216 {
4217 short scl_id = *p - SYNID_CLUSTER;
4218
4219 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004220 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004221 }
4222 else
4223 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4224 if (p[1])
4225 msg_putchar(',');
4226 }
4227 msg_putchar(' ');
4228}
4229
4230 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004231put_pattern(
4232 char *s,
4233 int c,
4234 synpat_T *spp,
4235 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004236{
4237 long n;
4238 int mask;
4239 int first;
4240 static char *sepchars = "/+=-#@\"|'^&";
4241 int i;
4242
4243 /* May have to write "matchgroup=group" */
4244 if (last_matchgroup != spp->sp_syn_match_id)
4245 {
4246 last_matchgroup = spp->sp_syn_match_id;
4247 msg_puts_attr((char_u *)"matchgroup", attr);
4248 msg_putchar('=');
4249 if (last_matchgroup == 0)
4250 msg_outtrans((char_u *)"NONE");
4251 else
4252 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4253 msg_putchar(' ');
4254 }
4255
4256 /* output the name of the pattern and an '=' or ' ' */
4257 msg_puts_attr((char_u *)s, attr);
4258 msg_putchar(c);
4259
4260 /* output the pattern, in between a char that is not in the pattern */
4261 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4262 if (sepchars[++i] == NUL)
4263 {
4264 i = 0; /* no good char found, just use the first one */
4265 break;
4266 }
4267 msg_putchar(sepchars[i]);
4268 msg_outtrans(spp->sp_pattern);
4269 msg_putchar(sepchars[i]);
4270
4271 /* output any pattern options */
4272 first = TRUE;
4273 for (i = 0; i < SPO_COUNT; ++i)
4274 {
4275 mask = (1 << i);
4276 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4277 {
4278 if (!first)
4279 msg_putchar(','); /* separate with commas */
4280 msg_puts((char_u *)spo_name_tab[i]);
4281 n = spp->sp_offsets[i];
4282 if (i != SPO_LC_OFF)
4283 {
4284 if (spp->sp_off_flags & mask)
4285 msg_putchar('s');
4286 else
4287 msg_putchar('e');
4288 if (n > 0)
4289 msg_putchar('+');
4290 }
4291 if (n || i == SPO_LC_OFF)
4292 msg_outnum(n);
4293 first = FALSE;
4294 }
4295 }
4296 msg_putchar(' ');
4297}
4298
4299/*
4300 * List or clear the keywords for one syntax group.
4301 * Return TRUE if the header has been printed.
4302 */
4303 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004304syn_list_keywords(
4305 int id,
4306 hashtab_T *ht,
4307 int did_header, /* header has already been printed */
4308 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004309{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004310 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004311 hashitem_T *hi;
4312 keyentry_T *kp;
4313 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004314 int prev_contained = 0;
4315 short *prev_next_list = NULL;
4316 short *prev_cont_in_list = NULL;
4317 int prev_skipnl = 0;
4318 int prev_skipwhite = 0;
4319 int prev_skipempty = 0;
4320
Bram Moolenaar071d4272004-06-13 20:20:40 +00004321 /*
4322 * Unfortunately, this list of keywords is not sorted on alphabet but on
4323 * hash value...
4324 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004325 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004326 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004327 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004328 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004329 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004330 --todo;
4331 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004332 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004333 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004334 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004335 if (prev_contained != (kp->flags & HL_CONTAINED)
4336 || prev_skipnl != (kp->flags & HL_SKIPNL)
4337 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4338 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4339 || prev_cont_in_list != kp->k_syn.cont_in_list
4340 || prev_next_list != kp->next_list)
4341 outlen = 9999;
4342 else
4343 outlen = (int)STRLEN(kp->keyword);
4344 /* output "contained" and "nextgroup" on each line */
4345 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004346 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004347 prev_contained = 0;
4348 prev_next_list = NULL;
4349 prev_cont_in_list = NULL;
4350 prev_skipnl = 0;
4351 prev_skipwhite = 0;
4352 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004354 did_header = TRUE;
4355 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004356 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004357 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004358 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004359 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004360 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004361 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004362 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004363 put_id_list((char_u *)"containedin",
4364 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004365 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004366 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004367 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004368 if (kp->next_list != prev_next_list)
4369 {
4370 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4371 msg_putchar(' ');
4372 prev_next_list = kp->next_list;
4373 if (kp->flags & HL_SKIPNL)
4374 {
4375 msg_puts_attr((char_u *)"skipnl", attr);
4376 msg_putchar(' ');
4377 prev_skipnl = (kp->flags & HL_SKIPNL);
4378 }
4379 if (kp->flags & HL_SKIPWHITE)
4380 {
4381 msg_puts_attr((char_u *)"skipwhite", attr);
4382 msg_putchar(' ');
4383 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4384 }
4385 if (kp->flags & HL_SKIPEMPTY)
4386 {
4387 msg_puts_attr((char_u *)"skipempty", attr);
4388 msg_putchar(' ');
4389 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4390 }
4391 }
4392 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004393 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004394 }
4395 }
4396 }
4397
4398 return did_header;
4399}
4400
4401 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004402syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004403{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004404 hashitem_T *hi;
4405 keyentry_T *kp;
4406 keyentry_T *kp_prev;
4407 keyentry_T *kp_next;
4408 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004409
Bram Moolenaardad6b692005-01-25 22:14:34 +00004410 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004411 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004412 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004413 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004414 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004415 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004416 --todo;
4417 kp_prev = NULL;
4418 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004419 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004420 if (kp->k_syn.id == id)
4421 {
4422 kp_next = kp->ke_next;
4423 if (kp_prev == NULL)
4424 {
4425 if (kp_next == NULL)
4426 hash_remove(ht, hi);
4427 else
4428 hi->hi_key = KE2HIKEY(kp_next);
4429 }
4430 else
4431 kp_prev->ke_next = kp_next;
4432 vim_free(kp->next_list);
4433 vim_free(kp->k_syn.cont_in_list);
4434 vim_free(kp);
4435 kp = kp_next;
4436 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004437 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004438 {
4439 kp_prev = kp;
4440 kp = kp->ke_next;
4441 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004442 }
4443 }
4444 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004445 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004446}
4447
4448/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004449 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004450 */
4451 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004452clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004453{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004454 hashitem_T *hi;
4455 int todo;
4456 keyentry_T *kp;
4457 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004458
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004459 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004460 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004461 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004462 if (!HASHITEM_EMPTY(hi))
4463 {
4464 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004465 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004466 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004467 kp_next = kp->ke_next;
4468 vim_free(kp->next_list);
4469 vim_free(kp->k_syn.cont_in_list);
4470 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004471 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004472 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004473 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004474 hash_clear(ht);
4475 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004476}
4477
4478/*
4479 * Add a keyword to the list of keywords.
4480 */
4481 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004482add_keyword(
4483 char_u *name, /* name of keyword */
4484 int id, /* group ID for this keyword */
4485 int flags, /* flags for this keyword */
4486 short *cont_in_list, /* containedin for this keyword */
4487 short *next_list, /* nextgroup for this keyword */
4488 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004489{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004490 keyentry_T *kp;
4491 hashtab_T *ht;
4492 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004493 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004494 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004495 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004496
Bram Moolenaar860cae12010-06-05 23:22:07 +02004497 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004498 name_ic = str_foldcase(name, (int)STRLEN(name),
4499 name_folded, MAXKEYWLEN + 1);
4500 else
4501 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004502 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4503 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004504 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004505 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004506 kp->k_syn.id = id;
4507 kp->k_syn.inc_tag = current_syn_inc_tag;
4508 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004509 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004510 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004511 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004512 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004513 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004514
Bram Moolenaar860cae12010-06-05 23:22:07 +02004515 if (curwin->w_s->b_syn_ic)
4516 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004517 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004518 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004519
Bram Moolenaardad6b692005-01-25 22:14:34 +00004520 hash = hash_hash(kp->keyword);
4521 hi = hash_lookup(ht, kp->keyword, hash);
4522 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004523 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004524 /* new keyword, add to hashtable */
4525 kp->ke_next = NULL;
4526 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004527 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004528 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004529 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004530 /* keyword already exists, prepend to list */
4531 kp->ke_next = HI2KE(hi);
4532 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004533 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004534}
4535
4536/*
4537 * Get the start and end of the group name argument.
4538 * Return a pointer to the first argument.
4539 * Return NULL if the end of the command was found instead of further args.
4540 */
4541 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004542get_group_name(
4543 char_u *arg, /* start of the argument */
4544 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004545{
4546 char_u *rest;
4547
4548 *name_end = skiptowhite(arg);
4549 rest = skipwhite(*name_end);
4550
4551 /*
4552 * Check if there are enough arguments. The first argument may be a
4553 * pattern, where '|' is allowed, so only check for NUL.
4554 */
4555 if (ends_excmd(*arg) || *rest == NUL)
4556 return NULL;
4557 return rest;
4558}
4559
4560/*
4561 * Check for syntax command option arguments.
4562 * This can be called at any place in the list of arguments, and just picks
4563 * out the arguments that are known. Can be called several times in a row to
4564 * collect all options in between other arguments.
4565 * Return a pointer to the next argument (which isn't an option).
4566 * Return NULL for any error;
4567 */
4568 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004569get_syn_options(
4570 char_u *arg, /* next argument to be checked */
4571 syn_opt_arg_T *opt, /* various things */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004572 int *conceal_char UNUSED,
4573 int skip) /* TRUE if skipping over command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004574{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004575 char_u *gname_start, *gname;
4576 int syn_id;
4577 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004578 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004579 int i;
4580 int fidx;
4581 static struct flag
4582 {
4583 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004584 int argtype;
4585 int flags;
4586 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4587 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4588 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4589 {"eExXtTeEnNdD", 0, HL_EXTEND},
4590 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4591 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4592 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4593 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4594 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4595 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4596 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4597 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4598 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004599 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4600 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4601 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004602 {"cCoOnNtTaAiInNsS", 1, 0},
4603 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4604 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004605 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004606 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004607
4608 if (arg == NULL) /* already detected error */
4609 return NULL;
4610
Bram Moolenaar860cae12010-06-05 23:22:07 +02004611#ifdef FEAT_CONCEAL
4612 if (curwin->w_s->b_syn_conceal)
4613 opt->flags |= HL_CONCEAL;
4614#endif
4615
Bram Moolenaar071d4272004-06-13 20:20:40 +00004616 for (;;)
4617 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004618 /*
4619 * This is used very often when a large number of keywords is defined.
4620 * Need to skip quickly when no option name is found.
4621 * Also avoid tolower(), it's slow.
4622 */
4623 if (strchr(first_letters, *arg) == NULL)
4624 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004625
4626 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4627 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004628 p = flagtab[fidx].name;
4629 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4630 if (arg[len] != p[i] && arg[len] != p[i + 1])
4631 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004632 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004633 || (flagtab[fidx].argtype > 0
4634 ? arg[len] == '='
4635 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004636 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004637 if (opt->keyword
4638 && (flagtab[fidx].flags == HL_DISPLAY
4639 || flagtab[fidx].flags == HL_FOLD
4640 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004641 /* treat "display", "fold" and "extend" as a keyword */
4642 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004643 break;
4644 }
4645 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004646 if (fidx < 0) /* no match found */
4647 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004648
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004649 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004650 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004651 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004652 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004653 emsg(_("E395: contains argument not accepted here"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004654 return NULL;
4655 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004656 if (get_id_list(&arg, 8, &opt->cont_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 == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004660 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004661 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004662 return NULL;
4663 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004664 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004665 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004666 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004667 return NULL;
4668 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004669 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4670 {
4671#ifdef FEAT_MBYTE
4672 /* cchar=? */
4673 if (has_mbyte)
4674 {
4675# ifdef FEAT_CONCEAL
4676 *conceal_char = mb_ptr2char(arg + 6);
4677# endif
4678 arg += mb_ptr2len(arg + 6) - 1;
4679 }
4680 else
4681#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004682 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004683#ifdef FEAT_CONCEAL
4684 *conceal_char = arg[6];
4685#else
4686 ;
4687#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004688 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004689#ifdef FEAT_CONCEAL
4690 if (!vim_isprintc_strict(*conceal_char))
4691 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004692 emsg(_("E844: invalid cchar value"));
Bram Moolenaar4124e722011-01-22 00:58:20 +01004693 return NULL;
4694 }
4695#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004696 arg = skipwhite(arg + 7);
4697 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004698 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004699 {
4700 opt->flags |= flagtab[fidx].flags;
4701 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004702
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004703 if (flagtab[fidx].flags == HL_SYNC_HERE
4704 || flagtab[fidx].flags == HL_SYNC_THERE)
4705 {
4706 if (opt->sync_idx == NULL)
4707 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004708 emsg(_("E393: group[t]here not accepted here"));
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004709 return NULL;
4710 }
4711 gname_start = arg;
4712 arg = skiptowhite(arg);
4713 if (gname_start == arg)
4714 return NULL;
4715 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4716 if (gname == NULL)
4717 return NULL;
4718 if (STRCMP(gname, "NONE") == 0)
4719 *opt->sync_idx = NONE_IDX;
4720 else
4721 {
4722 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004723 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4724 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4725 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004726 {
4727 *opt->sync_idx = i;
4728 break;
4729 }
4730 if (i < 0)
4731 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004732 semsg(_("E394: Didn't find region item for %s"), gname);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004733 vim_free(gname);
4734 return NULL;
4735 }
4736 }
4737
4738 vim_free(gname);
4739 arg = skipwhite(arg);
4740 }
4741#ifdef FEAT_FOLDING
4742 else if (flagtab[fidx].flags == HL_FOLD
4743 && foldmethodIsSyntax(curwin))
4744 /* Need to update folds later. */
4745 foldUpdateAll(curwin);
4746#endif
4747 }
4748 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004749
4750 return arg;
4751}
4752
4753/*
4754 * Adjustments to syntax item when declared in a ":syn include"'d file.
4755 * Set the contained flag, and if the item is not already contained, add it
4756 * to the specified top-level group, if any.
4757 */
4758 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004759syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004760{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004761 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004762 return;
4763 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004764 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004765 {
4766 /* We have to alloc this, because syn_combine_list() will free it. */
4767 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004768 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004769
4770 if (grp_list != NULL)
4771 {
4772 grp_list[0] = id;
4773 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004774 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004775 CLUSTER_ADD);
4776 }
4777 }
4778}
4779
4780/*
4781 * Handle ":syntax include [@{group-name}] filename" command.
4782 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004783 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004784syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004785{
4786 char_u *arg = eap->arg;
4787 int sgl_id = 1;
4788 char_u *group_name_end;
4789 char_u *rest;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004790 char *errormsg = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004791 int prev_toplvl_grp;
4792 int prev_syn_inc_tag;
4793 int source = FALSE;
4794
4795 eap->nextcmd = find_nextcmd(arg);
4796 if (eap->skip)
4797 return;
4798
4799 if (arg[0] == '@')
4800 {
4801 ++arg;
4802 rest = get_group_name(arg, &group_name_end);
4803 if (rest == NULL)
4804 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004805 emsg(_("E397: Filename required"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004806 return;
4807 }
4808 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004809 if (sgl_id == 0)
4810 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004811 /* separate_nextcmd() and expand_filename() depend on this */
4812 eap->arg = rest;
4813 }
4814
4815 /*
4816 * Everything that's left, up to the next command, should be the
4817 * filename to include.
4818 */
4819 eap->argt |= (XFILE | NOSPC);
4820 separate_nextcmd(eap);
4821 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4822 {
4823 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4824 * file. Need to expand the file name first. In other cases
4825 * ":runtime!" is used. */
4826 source = TRUE;
4827 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4828 {
4829 if (errormsg != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004830 emsg(errormsg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004831 return;
4832 }
4833 }
4834
4835 /*
4836 * Save and restore the existing top-level grouplist id and ":syn
4837 * include" tag around the actual inclusion.
4838 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004839 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4840 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004841 emsg(_("E847: Too many syntax includes"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004842 return;
4843 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004844 prev_syn_inc_tag = current_syn_inc_tag;
4845 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004846 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4847 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004848 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004849 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004850 semsg(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004851 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004852 current_syn_inc_tag = prev_syn_inc_tag;
4853}
4854
4855/*
4856 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4857 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004858 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004859syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004860{
4861 char_u *arg = eap->arg;
4862 char_u *group_name_end;
4863 int syn_id;
4864 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004865 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004866 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004867 char_u *kw;
4868 syn_opt_arg_T syn_opt_arg;
4869 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004870 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004871
4872 rest = get_group_name(arg, &group_name_end);
4873
4874 if (rest != NULL)
4875 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004876 if (eap->skip)
4877 syn_id = -1;
4878 else
4879 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004880 if (syn_id != 0)
4881 /* allocate a buffer, for removing backslashes in the keyword */
4882 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004883 if (keyword_copy != NULL)
4884 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004885 syn_opt_arg.flags = 0;
4886 syn_opt_arg.keyword = TRUE;
4887 syn_opt_arg.sync_idx = NULL;
4888 syn_opt_arg.has_cont_list = FALSE;
4889 syn_opt_arg.cont_in_list = NULL;
4890 syn_opt_arg.next_list = NULL;
4891
Bram Moolenaar071d4272004-06-13 20:20:40 +00004892 /*
4893 * The options given apply to ALL keywords, so all options must be
4894 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004895 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004896 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004897 cnt = 0;
4898 p = keyword_copy;
4899 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004900 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004901 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4902 eap->skip);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004903 if (rest == NULL || ends_excmd(*rest))
4904 break;
4905 /* Copy the keyword, removing backslashes, and add a NUL. */
Bram Moolenaar1c465442017-03-12 20:10:05 +01004906 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004907 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004908 if (*rest == '\\' && rest[1] != NUL)
4909 ++rest;
4910 *p++ = *rest++;
4911 }
4912 *p++ = NUL;
4913 ++cnt;
4914 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004915
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004916 if (!eap->skip)
4917 {
4918 /* Adjust flags for use of ":syn include". */
4919 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4920
4921 /*
4922 * 2: Add an entry for each keyword.
4923 */
4924 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4925 {
4926 for (p = vim_strchr(kw, '['); ; )
4927 {
4928 if (p != NULL)
4929 *p = NUL;
4930 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004931 syn_opt_arg.cont_in_list,
4932 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004933 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004934 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004935 if (p[1] == NUL)
4936 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004937 semsg(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004938 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004939 }
4940 if (p[1] == ']')
4941 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004942 if (p[2] != NUL)
4943 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004944 semsg(_("E890: trailing char after ']': %s]%s"),
Bram Moolenaar1560d072015-08-13 22:53:29 +02004945 kw, &p[2]);
4946 goto error;
4947 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004948 kw = p + 1; /* skip over the "]" */
4949 break;
4950 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004951#ifdef FEAT_MBYTE
4952 if (has_mbyte)
4953 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004954 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004955
4956 mch_memmove(p, p + 1, l);
4957 p += l;
4958 }
4959 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004960#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004961 {
4962 p[0] = p[1];
4963 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004964 }
4965 }
4966 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004967 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004968error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004969 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004970 vim_free(syn_opt_arg.cont_in_list);
4971 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004972 }
4973 }
4974
4975 if (rest != NULL)
4976 eap->nextcmd = check_nextcmd(rest);
4977 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004978 semsg(_(e_invarg2), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004979
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004980 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004981 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004982}
4983
4984/*
4985 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4986 *
4987 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4988 */
4989 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004990syn_cmd_match(
4991 exarg_T *eap,
4992 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004993{
4994 char_u *arg = eap->arg;
4995 char_u *group_name_end;
4996 char_u *rest;
4997 synpat_T item; /* the item found in the line */
4998 int syn_id;
4999 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005000 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005001 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005002 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005003
5004 /* Isolate the group name, check for validity */
5005 rest = get_group_name(arg, &group_name_end);
5006
5007 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005008 syn_opt_arg.flags = 0;
5009 syn_opt_arg.keyword = FALSE;
5010 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
5011 syn_opt_arg.has_cont_list = TRUE;
5012 syn_opt_arg.cont_list = NULL;
5013 syn_opt_arg.cont_in_list = NULL;
5014 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005015 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005016
5017 /* get the pattern. */
5018 init_syn_patterns();
5019 vim_memset(&item, 0, sizeof(item));
5020 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005021 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
5022 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005023
5024 /* Get options after the pattern */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005025 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005026
5027 if (rest != NULL) /* all arguments are valid */
5028 {
5029 /*
5030 * Check for trailing command and illegal trailing arguments.
5031 */
5032 eap->nextcmd = check_nextcmd(rest);
5033 if (!ends_excmd(*rest) || eap->skip)
5034 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005035 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005036 && (syn_id = syn_check_group(arg,
5037 (int)(group_name_end - arg))) != 0)
5038 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005039 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005040 /*
5041 * Store the pattern in the syn_items list
5042 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005043 idx = curwin->w_s->b_syn_patterns.ga_len;
5044 SYN_ITEMS(curwin->w_s)[idx] = item;
5045 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5046 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5047 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5048 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5049 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5050 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5051 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5052 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005053 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005054#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005055 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005056#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005057 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005058 curwin->w_s->b_syn_containedin = TRUE;
5059 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5060 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005061
5062 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005063 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005064 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005065#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005066 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005067 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005068#endif
5069
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005070 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005071 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005072 return; /* don't free the progs and patterns now */
5073 }
5074 }
5075
5076 /*
5077 * Something failed, free the allocated memory.
5078 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005079 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005080 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005081 vim_free(syn_opt_arg.cont_list);
5082 vim_free(syn_opt_arg.cont_in_list);
5083 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005084
5085 if (rest == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005086 semsg(_(e_invarg2), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005087}
5088
5089/*
5090 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5091 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5092 */
5093 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005094syn_cmd_region(
5095 exarg_T *eap,
5096 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005097{
5098 char_u *arg = eap->arg;
5099 char_u *group_name_end;
5100 char_u *rest; /* next arg, NULL on error */
5101 char_u *key_end;
5102 char_u *key = NULL;
5103 char_u *p;
5104 int item;
5105#define ITEM_START 0
5106#define ITEM_SKIP 1
5107#define ITEM_END 2
5108#define ITEM_MATCHGROUP 3
5109 struct pat_ptr
5110 {
5111 synpat_T *pp_synp; /* pointer to syn_pattern */
5112 int pp_matchgroup_id; /* matchgroup ID */
5113 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5114 } *(pat_ptrs[3]);
5115 /* patterns found in the line */
5116 struct pat_ptr *ppp;
5117 struct pat_ptr *ppp_next;
5118 int pat_count = 0; /* nr of syn_patterns found */
5119 int syn_id;
5120 int matchgroup_id = 0;
5121 int not_enough = FALSE; /* not enough arguments */
5122 int illegal = FALSE; /* illegal arguments */
5123 int success = FALSE;
5124 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005125 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005126 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005127
5128 /* Isolate the group name, check for validity */
5129 rest = get_group_name(arg, &group_name_end);
5130
5131 pat_ptrs[0] = NULL;
5132 pat_ptrs[1] = NULL;
5133 pat_ptrs[2] = NULL;
5134
5135 init_syn_patterns();
5136
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005137 syn_opt_arg.flags = 0;
5138 syn_opt_arg.keyword = FALSE;
5139 syn_opt_arg.sync_idx = NULL;
5140 syn_opt_arg.has_cont_list = TRUE;
5141 syn_opt_arg.cont_list = NULL;
5142 syn_opt_arg.cont_in_list = NULL;
5143 syn_opt_arg.next_list = NULL;
5144
Bram Moolenaar071d4272004-06-13 20:20:40 +00005145 /*
5146 * get the options, patterns and matchgroup.
5147 */
5148 while (rest != NULL && !ends_excmd(*rest))
5149 {
5150 /* Check for option arguments */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005151 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005152 if (rest == NULL || ends_excmd(*rest))
5153 break;
5154
5155 /* must be a pattern or matchgroup then */
5156 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005157 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005158 ++key_end;
5159 vim_free(key);
5160 key = vim_strnsave_up(rest, (int)(key_end - rest));
5161 if (key == NULL) /* out of memory */
5162 {
5163 rest = NULL;
5164 break;
5165 }
5166 if (STRCMP(key, "MATCHGROUP") == 0)
5167 item = ITEM_MATCHGROUP;
5168 else if (STRCMP(key, "START") == 0)
5169 item = ITEM_START;
5170 else if (STRCMP(key, "END") == 0)
5171 item = ITEM_END;
5172 else if (STRCMP(key, "SKIP") == 0)
5173 {
5174 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5175 {
5176 illegal = TRUE;
5177 break;
5178 }
5179 item = ITEM_SKIP;
5180 }
5181 else
5182 break;
5183 rest = skipwhite(key_end);
5184 if (*rest != '=')
5185 {
5186 rest = NULL;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005187 semsg(_("E398: Missing '=': %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005188 break;
5189 }
5190 rest = skipwhite(rest + 1);
5191 if (*rest == NUL)
5192 {
5193 not_enough = TRUE;
5194 break;
5195 }
5196
5197 if (item == ITEM_MATCHGROUP)
5198 {
5199 p = skiptowhite(rest);
5200 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5201 matchgroup_id = 0;
5202 else
5203 {
5204 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5205 if (matchgroup_id == 0)
5206 {
5207 illegal = TRUE;
5208 break;
5209 }
5210 }
5211 rest = skipwhite(p);
5212 }
5213 else
5214 {
5215 /*
5216 * Allocate room for a syn_pattern, and link it in the list of
5217 * syn_patterns for this item, at the start (because the list is
5218 * used from end to start).
5219 */
5220 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5221 if (ppp == NULL)
5222 {
5223 rest = NULL;
5224 break;
5225 }
5226 ppp->pp_next = pat_ptrs[item];
5227 pat_ptrs[item] = ppp;
5228 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5229 if (ppp->pp_synp == NULL)
5230 {
5231 rest = NULL;
5232 break;
5233 }
5234
5235 /*
5236 * Get the syntax pattern and the following offset(s).
5237 */
5238 /* Enable the appropriate \z specials. */
5239 if (item == ITEM_START)
5240 reg_do_extmatch = REX_SET;
5241 else if (item == ITEM_SKIP || item == ITEM_END)
5242 reg_do_extmatch = REX_USE;
5243 rest = get_syn_pattern(rest, ppp->pp_synp);
5244 reg_do_extmatch = 0;
5245 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005246 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005247 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5248 ppp->pp_matchgroup_id = matchgroup_id;
5249 ++pat_count;
5250 }
5251 }
5252 vim_free(key);
5253 if (illegal || not_enough)
5254 rest = NULL;
5255
5256 /*
5257 * Must have a "start" and "end" pattern.
5258 */
5259 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5260 pat_ptrs[ITEM_END] == NULL))
5261 {
5262 not_enough = TRUE;
5263 rest = NULL;
5264 }
5265
5266 if (rest != NULL)
5267 {
5268 /*
5269 * Check for trailing garbage or command.
5270 * If OK, add the item.
5271 */
5272 eap->nextcmd = check_nextcmd(rest);
5273 if (!ends_excmd(*rest) || eap->skip)
5274 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005275 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005276 && (syn_id = syn_check_group(arg,
5277 (int)(group_name_end - arg))) != 0)
5278 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005279 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005280 /*
5281 * Store the start/skip/end in the syn_items list
5282 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005283 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005284 for (item = ITEM_START; item <= ITEM_END; ++item)
5285 {
5286 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5287 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005288 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5289 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5290 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005291 (item == ITEM_START) ? SPTYPE_START :
5292 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005293 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5294 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005295 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5296 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005297 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005298 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005299#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005300 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005301#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005302 if (item == ITEM_START)
5303 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005304 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005305 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005306 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005307 syn_opt_arg.cont_in_list;
5308 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005309 curwin->w_s->b_syn_containedin = TRUE;
5310 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005311 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005312 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005313 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005314 ++idx;
5315#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005316 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005317 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005318#endif
5319 }
5320 }
5321
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005322 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005323 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005324 success = TRUE; /* don't free the progs and patterns now */
5325 }
5326 }
5327
5328 /*
5329 * Free the allocated memory.
5330 */
5331 for (item = ITEM_START; item <= ITEM_END; ++item)
5332 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5333 {
5334 if (!success)
5335 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005336 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005337 vim_free(ppp->pp_synp->sp_pattern);
5338 }
5339 vim_free(ppp->pp_synp);
5340 ppp_next = ppp->pp_next;
5341 vim_free(ppp);
5342 }
5343
5344 if (!success)
5345 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005346 vim_free(syn_opt_arg.cont_list);
5347 vim_free(syn_opt_arg.cont_in_list);
5348 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005349 if (not_enough)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005350 semsg(_("E399: Not enough arguments: syntax region %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005351 else if (illegal || rest == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005352 semsg(_(e_invarg2), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005353 }
5354}
5355
5356/*
5357 * A simple syntax group ID comparison function suitable for use in qsort()
5358 */
5359 static int
5360#ifdef __BORLANDC__
5361_RTLENTRYF
5362#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005363syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005364{
5365 const short *s1 = v1;
5366 const short *s2 = v2;
5367
5368 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5369}
5370
5371/*
5372 * Combines lists of syntax clusters.
5373 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5374 */
5375 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005376syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005377{
5378 int count1 = 0;
5379 int count2 = 0;
5380 short *g1;
5381 short *g2;
5382 short *clstr = NULL;
5383 int count;
5384 int round;
5385
5386 /*
5387 * Handle degenerate cases.
5388 */
5389 if (*clstr2 == NULL)
5390 return;
5391 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5392 {
5393 if (list_op == CLUSTER_REPLACE)
5394 vim_free(*clstr1);
5395 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5396 *clstr1 = *clstr2;
5397 else
5398 vim_free(*clstr2);
5399 return;
5400 }
5401
5402 for (g1 = *clstr1; *g1; g1++)
5403 ++count1;
5404 for (g2 = *clstr2; *g2; g2++)
5405 ++count2;
5406
5407 /*
5408 * For speed purposes, sort both lists.
5409 */
5410 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5411 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5412
5413 /*
5414 * We proceed in two passes; in round 1, we count the elements to place
5415 * in the new list, and in round 2, we allocate and populate the new
5416 * list. For speed, we use a mergesort-like method, adding the smaller
5417 * of the current elements in each list to the new list.
5418 */
5419 for (round = 1; round <= 2; round++)
5420 {
5421 g1 = *clstr1;
5422 g2 = *clstr2;
5423 count = 0;
5424
5425 /*
5426 * First, loop through the lists until one of them is empty.
5427 */
5428 while (*g1 && *g2)
5429 {
5430 /*
5431 * We always want to add from the first list.
5432 */
5433 if (*g1 < *g2)
5434 {
5435 if (round == 2)
5436 clstr[count] = *g1;
5437 count++;
5438 g1++;
5439 continue;
5440 }
5441 /*
5442 * We only want to add from the second list if we're adding the
5443 * lists.
5444 */
5445 if (list_op == CLUSTER_ADD)
5446 {
5447 if (round == 2)
5448 clstr[count] = *g2;
5449 count++;
5450 }
5451 if (*g1 == *g2)
5452 g1++;
5453 g2++;
5454 }
5455
5456 /*
5457 * Now add the leftovers from whichever list didn't get finished
5458 * first. As before, we only want to add from the second list if
5459 * we're adding the lists.
5460 */
5461 for (; *g1; g1++, count++)
5462 if (round == 2)
5463 clstr[count] = *g1;
5464 if (list_op == CLUSTER_ADD)
5465 for (; *g2; g2++, count++)
5466 if (round == 2)
5467 clstr[count] = *g2;
5468
5469 if (round == 1)
5470 {
5471 /*
5472 * If the group ended up empty, we don't need to allocate any
5473 * space for it.
5474 */
5475 if (count == 0)
5476 {
5477 clstr = NULL;
5478 break;
5479 }
5480 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5481 if (clstr == NULL)
5482 break;
5483 clstr[count] = 0;
5484 }
5485 }
5486
5487 /*
5488 * Finally, put the new list in place.
5489 */
5490 vim_free(*clstr1);
5491 vim_free(*clstr2);
5492 *clstr1 = clstr;
5493}
5494
5495/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005496 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005497 * If it is not found, 0 is returned.
5498 */
5499 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005500syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005501{
5502 int i;
5503 char_u *name_u;
5504
5505 /* Avoid using stricmp() too much, it's slow on some systems */
5506 name_u = vim_strsave_up(name);
5507 if (name_u == NULL)
5508 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005509 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5510 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5511 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005512 break;
5513 vim_free(name_u);
5514 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5515}
5516
5517/*
5518 * Like syn_scl_name2id(), but take a pointer + length argument.
5519 */
5520 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005521syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005522{
5523 char_u *name;
5524 int id = 0;
5525
5526 name = vim_strnsave(linep, len);
5527 if (name != NULL)
5528 {
5529 id = syn_scl_name2id(name);
5530 vim_free(name);
5531 }
5532 return id;
5533}
5534
5535/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005536 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005537 * The argument is a pointer to the name and the length of the name.
5538 * If it doesn't exist yet, a new entry is created.
5539 * Return 0 for failure.
5540 */
5541 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005542syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005543{
5544 int id;
5545 char_u *name;
5546
5547 name = vim_strnsave(pp, len);
5548 if (name == NULL)
5549 return 0;
5550
5551 id = syn_scl_name2id(name);
5552 if (id == 0) /* doesn't exist yet */
5553 id = syn_add_cluster(name);
5554 else
5555 vim_free(name);
5556 return id;
5557}
5558
5559/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005560 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005561 * "name" must be an allocated string, it will be consumed.
5562 * Return 0 for failure.
5563 */
5564 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005565syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005566{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005567 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005568
5569 /*
5570 * First call for this growarray: init growing array.
5571 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005572 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005573 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005574 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5575 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005576 }
5577
Bram Moolenaar42431a72011-04-01 14:44:59 +02005578 len = curwin->w_s->b_syn_clusters.ga_len;
5579 if (len >= MAX_CLUSTER_ID)
5580 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005581 emsg(_("E848: Too many syntax clusters"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02005582 vim_free(name);
5583 return 0;
5584 }
5585
Bram Moolenaar071d4272004-06-13 20:20:40 +00005586 /*
5587 * Make room for at least one other cluster entry.
5588 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005589 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005590 {
5591 vim_free(name);
5592 return 0;
5593 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005594
Bram Moolenaar860cae12010-06-05 23:22:07 +02005595 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5596 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5597 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5598 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5599 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005600
Bram Moolenaar217ad922005-03-20 22:37:15 +00005601 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005602 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005603 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005604 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005605
Bram Moolenaar071d4272004-06-13 20:20:40 +00005606 return len + SYNID_CLUSTER;
5607}
5608
5609/*
5610 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5611 * [add={groupname},..] [remove={groupname},..]".
5612 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005613 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005614syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005615{
5616 char_u *arg = eap->arg;
5617 char_u *group_name_end;
5618 char_u *rest;
5619 int scl_id;
5620 short *clstr_list;
5621 int got_clstr = FALSE;
5622 int opt_len;
5623 int list_op;
5624
5625 eap->nextcmd = find_nextcmd(arg);
5626 if (eap->skip)
5627 return;
5628
5629 rest = get_group_name(arg, &group_name_end);
5630
5631 if (rest != NULL)
5632 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005633 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5634 if (scl_id == 0)
5635 return;
5636 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005637
5638 for (;;)
5639 {
5640 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005641 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005642 {
5643 opt_len = 3;
5644 list_op = CLUSTER_ADD;
5645 }
5646 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005647 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005648 {
5649 opt_len = 6;
5650 list_op = CLUSTER_SUBTRACT;
5651 }
5652 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005653 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005654 {
5655 opt_len = 8;
5656 list_op = CLUSTER_REPLACE;
5657 }
5658 else
5659 break;
5660
5661 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005662 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005663 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005664 semsg(_(e_invarg2), rest);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005665 break;
5666 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005667 if (scl_id >= 0)
5668 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005669 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005670 else
5671 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005672 got_clstr = TRUE;
5673 }
5674
5675 if (got_clstr)
5676 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005677 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005678 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005679 }
5680 }
5681
5682 if (!got_clstr)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005683 emsg(_("E400: No cluster specified"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005684 if (rest == NULL || !ends_excmd(*rest))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005685 semsg(_(e_invarg2), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005686}
5687
5688/*
5689 * On first call for current buffer: Init growing array.
5690 */
5691 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005692init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005693{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005694 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5695 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005696}
5697
5698/*
5699 * Get one pattern for a ":syntax match" or ":syntax region" command.
5700 * Stores the pattern and program in a synpat_T.
5701 * Returns a pointer to the next argument, or NULL in case of an error.
5702 */
5703 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005704get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005705{
5706 char_u *end;
5707 int *p;
5708 int idx;
5709 char_u *cpo_save;
5710
5711 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005712 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005713 return NULL;
5714
5715 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5716 if (*end != *arg) /* end delimiter not found */
5717 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005718 semsg(_("E401: Pattern delimiter not found: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005719 return NULL;
5720 }
5721 /* store the pattern and compiled regexp program */
5722 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5723 return NULL;
5724
5725 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5726 cpo_save = p_cpo;
5727 p_cpo = (char_u *)"";
5728 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5729 p_cpo = cpo_save;
5730
5731 if (ci->sp_prog == NULL)
5732 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005733 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005734#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005735 syn_clear_time(&ci->sp_time);
5736#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005737
5738 /*
5739 * Check for a match, highlight or region offset.
5740 */
5741 ++end;
5742 do
5743 {
5744 for (idx = SPO_COUNT; --idx >= 0; )
5745 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5746 break;
5747 if (idx >= 0)
5748 {
5749 p = &(ci->sp_offsets[idx]);
5750 if (idx != SPO_LC_OFF)
5751 switch (end[3])
5752 {
5753 case 's': break;
5754 case 'b': break;
5755 case 'e': idx += SPO_COUNT; break;
5756 default: idx = -1; break;
5757 }
5758 if (idx >= 0)
5759 {
5760 ci->sp_off_flags |= (1 << idx);
5761 if (idx == SPO_LC_OFF) /* lc=99 */
5762 {
5763 end += 3;
5764 *p = getdigits(&end);
5765
5766 /* "lc=" offset automatically sets "ms=" offset */
5767 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5768 {
5769 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5770 ci->sp_offsets[SPO_MS_OFF] = *p;
5771 }
5772 }
5773 else /* yy=x+99 */
5774 {
5775 end += 4;
5776 if (*end == '+')
5777 {
5778 ++end;
5779 *p = getdigits(&end); /* positive offset */
5780 }
5781 else if (*end == '-')
5782 {
5783 ++end;
5784 *p = -getdigits(&end); /* negative offset */
5785 }
5786 }
5787 if (*end != ',')
5788 break;
5789 ++end;
5790 }
5791 }
5792 } while (idx >= 0);
5793
Bram Moolenaar1c465442017-03-12 20:10:05 +01005794 if (!ends_excmd(*end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005795 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005796 semsg(_("E402: Garbage after pattern: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005797 return NULL;
5798 }
5799 return skipwhite(end);
5800}
5801
5802/*
5803 * Handle ":syntax sync .." command.
5804 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005805 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005806syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005807{
5808 char_u *arg_start = eap->arg;
5809 char_u *arg_end;
5810 char_u *key = NULL;
5811 char_u *next_arg;
5812 int illegal = FALSE;
5813 int finished = FALSE;
5814 long n;
5815 char_u *cpo_save;
5816
5817 if (ends_excmd(*arg_start))
5818 {
5819 syn_cmd_list(eap, TRUE);
5820 return;
5821 }
5822
5823 while (!ends_excmd(*arg_start))
5824 {
5825 arg_end = skiptowhite(arg_start);
5826 next_arg = skipwhite(arg_end);
5827 vim_free(key);
5828 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5829 if (STRCMP(key, "CCOMMENT") == 0)
5830 {
5831 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005832 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005833 if (!ends_excmd(*next_arg))
5834 {
5835 arg_end = skiptowhite(next_arg);
5836 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005837 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005838 (int)(arg_end - next_arg));
5839 next_arg = skipwhite(arg_end);
5840 }
5841 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005842 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005843 }
5844 else if ( STRNCMP(key, "LINES", 5) == 0
5845 || STRNCMP(key, "MINLINES", 8) == 0
5846 || STRNCMP(key, "MAXLINES", 8) == 0
5847 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5848 {
5849 if (key[4] == 'S')
5850 arg_end = key + 6;
5851 else if (key[0] == 'L')
5852 arg_end = key + 11;
5853 else
5854 arg_end = key + 9;
5855 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5856 {
5857 illegal = TRUE;
5858 break;
5859 }
5860 n = getdigits(&arg_end);
5861 if (!eap->skip)
5862 {
5863 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005864 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005865 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005866 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005867 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005868 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005869 }
5870 }
5871 else if (STRCMP(key, "FROMSTART") == 0)
5872 {
5873 if (!eap->skip)
5874 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005875 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5876 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005877 }
5878 }
5879 else if (STRCMP(key, "LINECONT") == 0)
5880 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005881 if (*next_arg == NUL) /* missing pattern */
5882 {
5883 illegal = TRUE;
5884 break;
5885 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005886 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005887 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005888 emsg(_("E403: syntax sync: line continuations pattern specified twice"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005889 finished = TRUE;
5890 break;
5891 }
5892 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5893 if (*arg_end != *next_arg) /* end delimiter not found */
5894 {
5895 illegal = TRUE;
5896 break;
5897 }
5898
5899 if (!eap->skip)
5900 {
5901 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005902 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005903 (int)(arg_end - next_arg - 1))) == NULL)
5904 {
5905 finished = TRUE;
5906 break;
5907 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005908 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005909
5910 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5911 cpo_save = p_cpo;
5912 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005913 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005914 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005915 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005916#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005917 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5918#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005919
Bram Moolenaar860cae12010-06-05 23:22:07 +02005920 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005921 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01005922 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005923 finished = TRUE;
5924 break;
5925 }
5926 }
5927 next_arg = skipwhite(arg_end + 1);
5928 }
5929 else
5930 {
5931 eap->arg = next_arg;
5932 if (STRCMP(key, "MATCH") == 0)
5933 syn_cmd_match(eap, TRUE);
5934 else if (STRCMP(key, "REGION") == 0)
5935 syn_cmd_region(eap, TRUE);
5936 else if (STRCMP(key, "CLEAR") == 0)
5937 syn_cmd_clear(eap, TRUE);
5938 else
5939 illegal = TRUE;
5940 finished = TRUE;
5941 break;
5942 }
5943 arg_start = next_arg;
5944 }
5945 vim_free(key);
5946 if (illegal)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005947 semsg(_("E404: Illegal arguments: %s"), arg_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005948 else if (!finished)
5949 {
5950 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005951 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005952 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005953 }
5954}
5955
5956/*
5957 * Convert a line of highlight group names into a list of group ID numbers.
5958 * "arg" should point to the "contains" or "nextgroup" keyword.
5959 * "arg" is advanced to after the last group name.
5960 * Careful: the argument is modified (NULs added).
5961 * returns FAIL for some error, OK for success.
5962 */
5963 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005964get_id_list(
5965 char_u **arg,
5966 int keylen, /* length of keyword */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005967 short **list, /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00005968 NULL, the list is silently skipped! */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005969 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005970{
5971 char_u *p = NULL;
5972 char_u *end;
5973 int round;
5974 int count;
5975 int total_count = 0;
5976 short *retval = NULL;
5977 char_u *name;
5978 regmatch_T regmatch;
5979 int id;
5980 int i;
5981 int failed = FALSE;
5982
5983 /*
5984 * We parse the list twice:
5985 * round == 1: count the number of items, allocate the array.
5986 * round == 2: fill the array with the items.
5987 * In round 1 new groups may be added, causing the number of items to
5988 * grow when a regexp is used. In that case round 1 is done once again.
5989 */
5990 for (round = 1; round <= 2; ++round)
5991 {
5992 /*
5993 * skip "contains"
5994 */
5995 p = skipwhite(*arg + keylen);
5996 if (*p != '=')
5997 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005998 semsg(_("E405: Missing equal sign: %s"), *arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005999 break;
6000 }
6001 p = skipwhite(p + 1);
6002 if (ends_excmd(*p))
6003 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006004 semsg(_("E406: Empty argument: %s"), *arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006005 break;
6006 }
6007
6008 /*
6009 * parse the arguments after "contains"
6010 */
6011 count = 0;
6012 while (!ends_excmd(*p))
6013 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01006014 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006015 ;
6016 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
6017 if (name == NULL)
6018 {
6019 failed = TRUE;
6020 break;
6021 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006022 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006023 if ( STRCMP(name + 1, "ALLBUT") == 0
6024 || STRCMP(name + 1, "ALL") == 0
6025 || STRCMP(name + 1, "TOP") == 0
6026 || STRCMP(name + 1, "CONTAINED") == 0)
6027 {
6028 if (TOUPPER_ASC(**arg) != 'C')
6029 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006030 semsg(_("E407: %s not allowed here"), name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006031 failed = TRUE;
6032 vim_free(name);
6033 break;
6034 }
6035 if (count != 0)
6036 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006037 semsg(_("E408: %s must be first in contains list"),
Bram Moolenaard7a96152017-01-22 15:28:55 +01006038 name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006039 failed = TRUE;
6040 vim_free(name);
6041 break;
6042 }
6043 if (name[1] == 'A')
6044 id = SYNID_ALLBUT;
6045 else if (name[1] == 'T')
6046 id = SYNID_TOP;
6047 else
6048 id = SYNID_CONTAINED;
6049 id += current_syn_inc_tag;
6050 }
6051 else if (name[1] == '@')
6052 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01006053 if (skip)
6054 id = -1;
6055 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01006056 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006057 }
6058 else
6059 {
6060 /*
6061 * Handle full group name.
6062 */
6063 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6064 id = syn_check_group(name + 1, (int)(end - p));
6065 else
6066 {
6067 /*
6068 * Handle match of regexp with group names.
6069 */
6070 *name = '^';
6071 STRCAT(name, "$");
6072 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6073 if (regmatch.regprog == NULL)
6074 {
6075 failed = TRUE;
6076 vim_free(name);
6077 break;
6078 }
6079
6080 regmatch.rm_ic = TRUE;
6081 id = 0;
6082 for (i = highlight_ga.ga_len; --i >= 0; )
6083 {
6084 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6085 (colnr_T)0))
6086 {
6087 if (round == 2)
6088 {
6089 /* Got more items than expected; can happen
6090 * when adding items that match:
6091 * "contains=a.*b,axb".
6092 * Go back to first round */
6093 if (count >= total_count)
6094 {
6095 vim_free(retval);
6096 round = 1;
6097 }
6098 else
6099 retval[count] = i + 1;
6100 }
6101 ++count;
6102 id = -1; /* remember that we found one */
6103 }
6104 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006105 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006106 }
6107 }
6108 vim_free(name);
6109 if (id == 0)
6110 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006111 semsg(_("E409: Unknown group name: %s"), p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006112 failed = TRUE;
6113 break;
6114 }
6115 if (id > 0)
6116 {
6117 if (round == 2)
6118 {
6119 /* Got more items than expected, go back to first round */
6120 if (count >= total_count)
6121 {
6122 vim_free(retval);
6123 round = 1;
6124 }
6125 else
6126 retval[count] = id;
6127 }
6128 ++count;
6129 }
6130 p = skipwhite(end);
6131 if (*p != ',')
6132 break;
6133 p = skipwhite(p + 1); /* skip comma in between arguments */
6134 }
6135 if (failed)
6136 break;
6137 if (round == 1)
6138 {
6139 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6140 if (retval == NULL)
6141 break;
6142 retval[count] = 0; /* zero means end of the list */
6143 total_count = count;
6144 }
6145 }
6146
6147 *arg = p;
6148 if (failed || retval == NULL)
6149 {
6150 vim_free(retval);
6151 return FAIL;
6152 }
6153
6154 if (*list == NULL)
6155 *list = retval;
6156 else
6157 vim_free(retval); /* list already found, don't overwrite it */
6158
6159 return OK;
6160}
6161
6162/*
6163 * Make a copy of an ID list.
6164 */
6165 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006166copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006167{
6168 int len;
6169 int count;
6170 short *retval;
6171
6172 if (list == NULL)
6173 return NULL;
6174
6175 for (count = 0; list[count]; ++count)
6176 ;
6177 len = (count + 1) * sizeof(short);
6178 retval = (short *)alloc((unsigned)len);
6179 if (retval != NULL)
6180 mch_memmove(retval, list, (size_t)len);
6181
6182 return retval;
6183}
6184
6185/*
6186 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6187 * "cur_si" can be NULL if not checking the "containedin" list.
6188 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6189 * the current item.
6190 * This function is called very often, keep it fast!!
6191 */
6192 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006193in_id_list(
6194 stateitem_T *cur_si, /* current item or NULL */
6195 short *list, /* id list */
6196 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6197 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006198{
6199 int retval;
6200 short *scl_list;
6201 short item;
6202 short id = ssp->id;
6203 static int depth = 0;
6204 int r;
6205
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006206 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006207 if (cur_si != NULL && ssp->cont_in_list != NULL
6208 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006209 {
6210 /* Ignore transparent items without a contains argument. Double check
6211 * that we don't go back past the first one. */
6212 while ((cur_si->si_flags & HL_TRANS_CONT)
6213 && cur_si > (stateitem_T *)(current_state.ga_data))
6214 --cur_si;
6215 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6216 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006217 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6218 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006219 return TRUE;
6220 }
6221
6222 if (list == NULL)
6223 return FALSE;
6224
6225 /*
6226 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6227 * inside anything. Only allow not-contained groups.
6228 */
6229 if (list == ID_LIST_ALL)
6230 return !contained;
6231
6232 /*
6233 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6234 * contains list. We also require that "id" is at the same ":syn include"
6235 * level as the list.
6236 */
6237 item = *list;
6238 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6239 {
6240 if (item < SYNID_TOP)
6241 {
6242 /* ALL or ALLBUT: accept all groups in the same file */
6243 if (item - SYNID_ALLBUT != ssp->inc_tag)
6244 return FALSE;
6245 }
6246 else if (item < SYNID_CONTAINED)
6247 {
6248 /* TOP: accept all not-contained groups in the same file */
6249 if (item - SYNID_TOP != ssp->inc_tag || contained)
6250 return FALSE;
6251 }
6252 else
6253 {
6254 /* CONTAINED: accept all contained groups in the same file */
6255 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6256 return FALSE;
6257 }
6258 item = *++list;
6259 retval = FALSE;
6260 }
6261 else
6262 retval = TRUE;
6263
6264 /*
6265 * Return "retval" if id is in the contains list.
6266 */
6267 while (item != 0)
6268 {
6269 if (item == id)
6270 return retval;
6271 if (item >= SYNID_CLUSTER)
6272 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006273 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006274 /* restrict recursiveness to 30 to avoid an endless loop for a
6275 * cluster that includes itself (indirectly) */
6276 if (scl_list != NULL && depth < 30)
6277 {
6278 ++depth;
6279 r = in_id_list(NULL, scl_list, ssp, contained);
6280 --depth;
6281 if (r)
6282 return retval;
6283 }
6284 }
6285 item = *++list;
6286 }
6287 return !retval;
6288}
6289
6290struct subcommand
6291{
Bram Moolenaard99df422016-01-29 23:20:40 +01006292 char *name; /* subcommand name */
6293 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006294};
6295
6296static struct subcommand subcommands[] =
6297{
6298 {"case", syn_cmd_case},
6299 {"clear", syn_cmd_clear},
6300 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006301 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006302 {"enable", syn_cmd_enable},
6303 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006304 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006305 {"keyword", syn_cmd_keyword},
6306 {"list", syn_cmd_list},
6307 {"manual", syn_cmd_manual},
6308 {"match", syn_cmd_match},
6309 {"on", syn_cmd_on},
6310 {"off", syn_cmd_off},
6311 {"region", syn_cmd_region},
6312 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006313 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006314 {"sync", syn_cmd_sync},
6315 {"", syn_cmd_list},
6316 {NULL, NULL}
6317};
6318
6319/*
6320 * ":syntax".
6321 * This searches the subcommands[] table for the subcommand name, and calls a
6322 * syntax_subcommand() function to do the rest.
6323 */
6324 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006325ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006326{
6327 char_u *arg = eap->arg;
6328 char_u *subcmd_end;
6329 char_u *subcmd_name;
6330 int i;
6331
6332 syn_cmdlinep = eap->cmdlinep;
6333
6334 /* isolate subcommand name */
6335 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6336 ;
6337 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6338 if (subcmd_name != NULL)
6339 {
6340 if (eap->skip) /* skip error messages for all subcommands */
6341 ++emsg_skip;
6342 for (i = 0; ; ++i)
6343 {
6344 if (subcommands[i].name == NULL)
6345 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006346 semsg(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006347 break;
6348 }
6349 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6350 {
6351 eap->arg = skipwhite(subcmd_end);
6352 (subcommands[i].func)(eap, FALSE);
6353 break;
6354 }
6355 }
6356 vim_free(subcmd_name);
6357 if (eap->skip)
6358 --emsg_skip;
6359 }
6360}
6361
Bram Moolenaar860cae12010-06-05 23:22:07 +02006362 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006363ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006364{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006365 char_u *old_value;
6366 char_u *new_value;
6367
Bram Moolenaar860cae12010-06-05 23:22:07 +02006368 if (curwin->w_s == &curwin->w_buffer->b_s)
6369 {
6370 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6371 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006372 hash_init(&curwin->w_s->b_keywtab);
6373 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006374#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006375 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006376 curwin->w_p_spell = FALSE; /* No spell checking */
6377 clear_string_option(&curwin->w_s->b_p_spc);
6378 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006379 clear_string_option(&curwin->w_s->b_p_spl);
6380#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006381 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006382 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006383
6384 /* save value of b:current_syntax */
6385 old_value = get_var_value((char_u *)"b:current_syntax");
6386 if (old_value != NULL)
6387 old_value = vim_strsave(old_value);
6388
6389 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6390 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006391 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006392
6393 /* move value of b:current_syntax to w:current_syntax */
6394 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006395 if (new_value != NULL)
6396 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006397
6398 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006399 if (old_value == NULL)
6400 do_unlet((char_u *)"b:current_syntax", TRUE);
6401 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006402 {
6403 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6404 vim_free(old_value);
6405 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006406}
6407
6408 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006409syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006410{
6411 return (win->w_s->b_syn_patterns.ga_len != 0
6412 || win->w_s->b_syn_clusters.ga_len != 0
6413 || win->w_s->b_keywtab.ht_used > 0
6414 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006415}
6416
6417#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6418
6419static enum
6420{
6421 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006422 EXP_CASE, /* expand ":syn case" arguments */
6423 EXP_SPELL, /* expand ":syn spell" arguments */
6424 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006425} expand_what;
6426
Bram Moolenaar4f688582007-07-24 12:34:30 +00006427/*
6428 * Reset include_link, include_default, include_none to 0.
6429 * Called when we are done expanding.
6430 */
6431 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006432reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006433{
6434 include_link = include_default = include_none = 0;
6435}
6436
6437/*
6438 * Handle command line completion for :match and :echohl command: Add "None"
6439 * as highlight group.
6440 */
6441 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006442set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006443{
6444 xp->xp_context = EXPAND_HIGHLIGHT;
6445 xp->xp_pattern = arg;
6446 include_none = 1;
6447}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006448
6449/*
6450 * Handle command line completion for :syntax command.
6451 */
6452 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006453set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006454{
6455 char_u *p;
6456
6457 /* Default: expand subcommands */
6458 xp->xp_context = EXPAND_SYNTAX;
6459 expand_what = EXP_SUBCMD;
6460 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006461 include_link = 0;
6462 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006463
6464 /* (part of) subcommand already typed */
6465 if (*arg != NUL)
6466 {
6467 p = skiptowhite(arg);
6468 if (*p != NUL) /* past first word */
6469 {
6470 xp->xp_pattern = skipwhite(p);
6471 if (*skiptowhite(xp->xp_pattern) != NUL)
6472 xp->xp_context = EXPAND_NOTHING;
6473 else if (STRNICMP(arg, "case", p - arg) == 0)
6474 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006475 else if (STRNICMP(arg, "spell", p - arg) == 0)
6476 expand_what = EXP_SPELL;
6477 else if (STRNICMP(arg, "sync", p - arg) == 0)
6478 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006479 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6480 || STRNICMP(arg, "region", p - arg) == 0
6481 || STRNICMP(arg, "match", p - arg) == 0
6482 || STRNICMP(arg, "list", p - arg) == 0)
6483 xp->xp_context = EXPAND_HIGHLIGHT;
6484 else
6485 xp->xp_context = EXPAND_NOTHING;
6486 }
6487 }
6488}
6489
Bram Moolenaar071d4272004-06-13 20:20:40 +00006490/*
6491 * Function given to ExpandGeneric() to obtain the list syntax names for
6492 * expansion.
6493 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006494 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006495get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006496{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006497 switch (expand_what)
6498 {
6499 case EXP_SUBCMD:
6500 return (char_u *)subcommands[idx].name;
6501 case EXP_CASE:
6502 {
6503 static char *case_args[] = {"match", "ignore", NULL};
6504 return (char_u *)case_args[idx];
6505 }
6506 case EXP_SPELL:
6507 {
6508 static char *spell_args[] =
6509 {"toplevel", "notoplevel", "default", NULL};
6510 return (char_u *)spell_args[idx];
6511 }
6512 case EXP_SYNC:
6513 {
6514 static char *sync_args[] =
6515 {"ccomment", "clear", "fromstart",
6516 "linebreaks=", "linecont", "lines=", "match",
6517 "maxlines=", "minlines=", "region", NULL};
6518 return (char_u *)sync_args[idx];
6519 }
6520 }
6521 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006522}
6523
6524#endif /* FEAT_CMDL_COMPL */
6525
Bram Moolenaar071d4272004-06-13 20:20:40 +00006526/*
6527 * Function called for expression evaluation: get syntax ID at file position.
6528 */
6529 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006530syn_get_id(
6531 win_T *wp,
6532 long lnum,
6533 colnr_T col,
6534 int trans, /* remove transparency */
6535 int *spellp, /* return: can do spell checking */
6536 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006537{
6538 /* When the position is not after the current position and in the same
6539 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006540 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006541 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006542 || col < current_col)
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006543 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006544 else if (wp->w_buffer == syn_buf
6545 && lnum == current_lnum
6546 && col > current_col)
6547 /* next_match may not be correct when moving around, e.g. with the
6548 * "skip" expression in searchpair() */
6549 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006550
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006551 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006552
6553 return (trans ? current_trans_id : current_id);
6554}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006555
Bram Moolenaar860cae12010-06-05 23:22:07 +02006556#if defined(FEAT_CONCEAL) || defined(PROTO)
6557/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006558 * Get extra information about the syntax item. Must be called right after
6559 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006560 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006561 * Returns the current flags.
6562 */
6563 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006564get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006565{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006566 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006567 return current_flags;
6568}
6569
6570/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006571 * Return conceal substitution character
6572 */
6573 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006574syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006575{
6576 return current_sub_char;
6577}
6578#endif
6579
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006580#if defined(FEAT_EVAL) || defined(PROTO)
6581/*
6582 * Return the syntax ID at position "i" in the current stack.
6583 * The caller must have called syn_get_id() before to fill the stack.
6584 * Returns -1 when "i" is out of range.
6585 */
6586 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006587syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006588{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006589 if (i >= current_state.ga_len)
6590 {
6591 /* Need to invalidate the state, because we didn't properly finish it
6592 * for the last character, "keep_state" was TRUE. */
6593 invalidate_current_state();
6594 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006595 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006596 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006597 return CUR_STATE(i).si_id;
6598}
6599#endif
6600
Bram Moolenaar071d4272004-06-13 20:20:40 +00006601#if defined(FEAT_FOLDING) || defined(PROTO)
6602/*
6603 * Function called to get folding level for line "lnum" in window "wp".
6604 */
6605 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006606syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006607{
6608 int level = 0;
6609 int i;
6610
6611 /* Return quickly when there are no fold items at all. */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006612 if (wp->w_s->b_syn_folditems != 0
6613 && !wp->w_s->b_syn_error
6614# ifdef SYN_TIME_LIMIT
6615 && !wp->w_s->b_syn_slow
6616# endif
6617 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006618 {
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006619 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006620
6621 for (i = 0; i < current_state.ga_len; ++i)
6622 if (CUR_STATE(i).si_flags & HL_FOLD)
6623 ++level;
6624 }
6625 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006626 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006627 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006628 if (level < 0)
6629 level = 0;
6630 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006631 return level;
6632}
6633#endif
6634
Bram Moolenaar01615492015-02-03 13:00:38 +01006635#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006636/*
6637 * ":syntime".
6638 */
6639 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006640ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006641{
6642 if (STRCMP(eap->arg, "on") == 0)
6643 syn_time_on = TRUE;
6644 else if (STRCMP(eap->arg, "off") == 0)
6645 syn_time_on = FALSE;
6646 else if (STRCMP(eap->arg, "clear") == 0)
6647 syntime_clear();
6648 else if (STRCMP(eap->arg, "report") == 0)
6649 syntime_report();
6650 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01006651 semsg(_(e_invarg2), eap->arg);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006652}
6653
6654 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006655syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006656{
6657 profile_zero(&st->total);
6658 profile_zero(&st->slowest);
6659 st->count = 0;
6660 st->match = 0;
6661}
6662
6663/*
6664 * Clear the syntax timing for the current buffer.
6665 */
6666 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006667syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006668{
6669 int idx;
6670 synpat_T *spp;
6671
6672 if (!syntax_present(curwin))
6673 {
6674 MSG(_(msg_no_items));
6675 return;
6676 }
6677 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6678 {
6679 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6680 syn_clear_time(&spp->sp_time);
6681 }
6682}
6683
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006684#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6685/*
6686 * Function given to ExpandGeneric() to obtain the possible arguments of the
6687 * ":syntime {on,off,clear,report}" command.
6688 */
6689 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006690get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006691{
6692 switch (idx)
6693 {
6694 case 0: return (char_u *)"on";
6695 case 1: return (char_u *)"off";
6696 case 2: return (char_u *)"clear";
6697 case 3: return (char_u *)"report";
6698 }
6699 return NULL;
6700}
6701#endif
6702
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006703typedef struct
6704{
6705 proftime_T total;
6706 int count;
6707 int match;
6708 proftime_T slowest;
6709 proftime_T average;
6710 int id;
6711 char_u *pattern;
6712} time_entry_T;
6713
6714 static int
6715#ifdef __BORLANDC__
6716_RTLENTRYF
6717#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006718syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006719{
6720 const time_entry_T *s1 = v1;
6721 const time_entry_T *s2 = v2;
6722
6723 return profile_cmp(&s1->total, &s2->total);
6724}
6725
6726/*
6727 * Clear the syntax timing for the current buffer.
6728 */
6729 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006730syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006731{
6732 int idx;
6733 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006734# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006735 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006736# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006737 int len;
6738 proftime_T total_total;
6739 int total_count = 0;
6740 garray_T ga;
6741 time_entry_T *p;
6742
6743 if (!syntax_present(curwin))
6744 {
6745 MSG(_(msg_no_items));
6746 return;
6747 }
6748
6749 ga_init2(&ga, sizeof(time_entry_T), 50);
6750 profile_zero(&total_total);
6751 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6752 {
6753 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6754 if (spp->sp_time.count > 0)
6755 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006756 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006757 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6758 p->total = spp->sp_time.total;
6759 profile_add(&total_total, &spp->sp_time.total);
6760 p->count = spp->sp_time.count;
6761 p->match = spp->sp_time.match;
6762 total_count += spp->sp_time.count;
6763 p->slowest = spp->sp_time.slowest;
6764# ifdef FEAT_FLOAT
6765 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6766 p->average = tm;
6767# endif
6768 p->id = spp->sp_syn.id;
6769 p->pattern = spp->sp_pattern;
6770 ++ga.ga_len;
6771 }
6772 }
6773
Bram Moolenaara2162552017-01-08 17:46:20 +01006774 /* Sort on total time. Skip if there are no items to avoid passing NULL
6775 * pointer to qsort(). */
6776 if (ga.ga_len > 1)
6777 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006778 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006779
6780 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6781 MSG_PUTS("\n");
6782 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6783 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006784 p = ((time_entry_T *)ga.ga_data) + idx;
6785
6786 MSG_PUTS(profile_msg(&p->total));
6787 MSG_PUTS(" "); /* make sure there is always a separating space */
6788 msg_advance(13);
6789 msg_outnum(p->count);
6790 MSG_PUTS(" ");
6791 msg_advance(20);
6792 msg_outnum(p->match);
6793 MSG_PUTS(" ");
6794 msg_advance(26);
6795 MSG_PUTS(profile_msg(&p->slowest));
6796 MSG_PUTS(" ");
6797 msg_advance(38);
6798# ifdef FEAT_FLOAT
6799 MSG_PUTS(profile_msg(&p->average));
6800 MSG_PUTS(" ");
6801# endif
6802 msg_advance(50);
6803 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6804 MSG_PUTS(" ");
6805
6806 msg_advance(69);
6807 if (Columns < 80)
6808 len = 20; /* will wrap anyway */
6809 else
6810 len = Columns - 70;
6811 if (len > (int)STRLEN(p->pattern))
6812 len = (int)STRLEN(p->pattern);
6813 msg_outtrans_len(p->pattern, len);
6814 MSG_PUTS("\n");
6815 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006816 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006817 if (!got_int)
6818 {
6819 MSG_PUTS("\n");
6820 MSG_PUTS(profile_msg(&total_total));
6821 msg_advance(13);
6822 msg_outnum(total_count);
6823 MSG_PUTS("\n");
6824 }
6825}
6826#endif
6827
Bram Moolenaar071d4272004-06-13 20:20:40 +00006828#endif /* FEAT_SYN_HL */
6829
Bram Moolenaar071d4272004-06-13 20:20:40 +00006830/**************************************
6831 * Highlighting stuff *
6832 **************************************/
6833
6834/*
6835 * The default highlight groups. These are compiled-in for fast startup and
6836 * they still work when the runtime files can't be found.
6837 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006838 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6839 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006840 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006841#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006842# define CENT(a, b) b
6843#else
6844# define CENT(a, b) a
6845#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006846static char *(highlight_init_both[]) = {
6847 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6848 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6849 CENT("IncSearch term=reverse cterm=reverse",
6850 "IncSearch term=reverse cterm=reverse gui=reverse"),
6851 CENT("ModeMsg term=bold cterm=bold",
6852 "ModeMsg term=bold cterm=bold gui=bold"),
6853 CENT("NonText term=bold ctermfg=Blue",
6854 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6855 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6856 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6857 CENT("StatusLineNC term=reverse cterm=reverse",
6858 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
6859 "default link EndOfBuffer NonText",
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006860 CENT("VertSplit term=reverse cterm=reverse",
6861 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006862#ifdef FEAT_CLIPBOARD
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006863 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6864 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006865#endif
6866#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006867 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6868 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006869#endif
6870#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006871 CENT("PmenuSbar ctermbg=Grey",
6872 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006873#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006874 CENT("TabLineSel term=bold cterm=bold",
6875 "TabLineSel term=bold cterm=bold gui=bold"),
6876 CENT("TabLineFill term=reverse cterm=reverse",
6877 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00006878#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006879 "Cursor guibg=fg guifg=bg",
6880 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006881#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006882 "default link QuickFixLine Search",
6883 NULL
6884};
Bram Moolenaar071d4272004-06-13 20:20:40 +00006885
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006886/* Default colors only used with a light background. */
6887static char *(highlight_init_light[]) = {
6888 CENT("Directory term=bold ctermfg=DarkBlue",
6889 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6890 CENT("LineNr term=underline ctermfg=Brown",
6891 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6892 CENT("CursorLineNr term=bold ctermfg=Brown",
6893 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
6894 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6895 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6896 CENT("Question term=standout ctermfg=DarkGreen",
6897 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6898 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6899 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006900#ifdef FEAT_SPELL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006901 CENT("SpellBad term=reverse ctermbg=LightRed",
6902 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6903 CENT("SpellCap term=reverse ctermbg=LightBlue",
6904 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6905 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6906 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6907 CENT("SpellLocal term=underline ctermbg=Cyan",
6908 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006909#endif
6910#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006911 CENT("PmenuThumb ctermbg=Black",
6912 "PmenuThumb ctermbg=Black guibg=Black"),
6913 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6914 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6915 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6916 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006917#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006918 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6919 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6920 CENT("Title term=bold ctermfg=DarkMagenta",
6921 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6922 CENT("WarningMsg term=standout ctermfg=DarkRed",
6923 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006924#ifdef FEAT_WILDMENU
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006925 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6926 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006927#endif
6928#ifdef FEAT_FOLDING
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006929 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6930 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6931 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6932 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006933#endif
6934#ifdef FEAT_SIGNS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006935 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6936 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006937#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006938 CENT("Visual term=reverse",
6939 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006940#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006941 CENT("DiffAdd term=bold ctermbg=LightBlue",
6942 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6943 CENT("DiffChange term=bold ctermbg=LightMagenta",
6944 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6945 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6946 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006947#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006948 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6949 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006950#ifdef FEAT_SYN_HL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006951 CENT("CursorColumn term=reverse ctermbg=LightGrey",
6952 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
6953 CENT("CursorLine term=underline cterm=underline",
6954 "CursorLine term=underline cterm=underline guibg=Grey90"),
6955 CENT("ColorColumn term=reverse ctermbg=LightRed",
6956 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006957#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006958#ifdef FEAT_CONCEAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006959 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6960 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02006961#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006962 CENT("MatchParen term=reverse ctermbg=Cyan",
6963 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006964#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006965 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006966#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006967#ifdef FEAT_TERMINAL
6968 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
6969 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
6970 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
6971 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
6972#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02006973#ifdef FEAT_MENU
6974 CENT("ToolbarLine term=underline ctermbg=LightGrey",
6975 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
6976 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006977 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02006978#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006979 NULL
6980};
Bram Moolenaar071d4272004-06-13 20:20:40 +00006981
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006982/* Default colors only used with a dark background. */
6983static char *(highlight_init_dark[]) = {
6984 CENT("Directory term=bold ctermfg=LightCyan",
6985 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6986 CENT("LineNr term=underline ctermfg=Yellow",
6987 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6988 CENT("CursorLineNr term=bold ctermfg=Yellow",
6989 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
6990 CENT("MoreMsg term=bold ctermfg=LightGreen",
6991 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6992 CENT("Question term=standout ctermfg=LightGreen",
6993 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6994 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6995 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6996 CENT("SpecialKey term=bold ctermfg=LightBlue",
6997 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006998#ifdef FEAT_SPELL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006999 CENT("SpellBad term=reverse ctermbg=Red",
7000 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
7001 CENT("SpellCap term=reverse ctermbg=Blue",
7002 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
7003 CENT("SpellRare term=reverse ctermbg=Magenta",
7004 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
7005 CENT("SpellLocal term=underline ctermbg=Cyan",
7006 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007007#endif
7008#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007009 CENT("PmenuThumb ctermbg=White",
7010 "PmenuThumb ctermbg=White guibg=White"),
7011 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
7012 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
7013 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
7014 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007015#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007016 CENT("Title term=bold ctermfg=LightMagenta",
7017 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
7018 CENT("WarningMsg term=standout ctermfg=LightRed",
7019 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007020#ifdef FEAT_WILDMENU
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007021 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
7022 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007023#endif
7024#ifdef FEAT_FOLDING
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007025 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
7026 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
7027 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7028 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007029#endif
7030#ifdef FEAT_SIGNS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007031 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7032 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007033#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007034 CENT("Visual term=reverse",
7035 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007036#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007037 CENT("DiffAdd term=bold ctermbg=DarkBlue",
7038 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
7039 CENT("DiffChange term=bold ctermbg=DarkMagenta",
7040 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
7041 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
7042 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007043#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007044 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7045 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007046#ifdef FEAT_SYN_HL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007047 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
7048 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
7049 CENT("CursorLine term=underline cterm=underline",
7050 "CursorLine term=underline cterm=underline guibg=Grey40"),
7051 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7052 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007053#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007054 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7055 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02007056#ifdef FEAT_CONCEAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007057 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7058 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02007059#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007060#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007061 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007062#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007063#ifdef FEAT_TERMINAL
7064 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
7065 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
7066 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
7067 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
7068#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007069#ifdef FEAT_MENU
7070 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02007071 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007072 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
7073 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
7074#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007075 NULL
7076};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007077
7078 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007079init_highlight(
7080 int both, /* include groups where 'bg' doesn't matter */
7081 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007082{
7083 int i;
7084 char **pp;
7085 static int had_both = FALSE;
7086#ifdef FEAT_EVAL
7087 char_u *p;
7088
7089 /*
7090 * Try finding the color scheme file. Used when a color file was loaded
7091 * and 'background' or 't_Co' is changed.
7092 */
7093 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007094 if (p != NULL)
7095 {
7096 /* The value of g:colors_name could be freed when sourcing the script,
7097 * making "p" invalid, so copy it. */
7098 char_u *copy_p = vim_strsave(p);
7099 int r;
7100
7101 if (copy_p != NULL)
7102 {
7103 r = load_colors(copy_p);
7104 vim_free(copy_p);
7105 if (r == OK)
7106 return;
7107 }
7108 }
7109
Bram Moolenaar071d4272004-06-13 20:20:40 +00007110#endif
7111
7112 /*
7113 * Didn't use a color file, use the compiled-in colors.
7114 */
7115 if (both)
7116 {
7117 had_both = TRUE;
7118 pp = highlight_init_both;
7119 for (i = 0; pp[i] != NULL; ++i)
7120 do_highlight((char_u *)pp[i], reset, TRUE);
7121 }
7122 else if (!had_both)
7123 /* Don't do anything before the call with both == TRUE from main().
7124 * Not everything has been setup then, and that call will overrule
7125 * everything anyway. */
7126 return;
7127
7128 if (*p_bg == 'l')
7129 pp = highlight_init_light;
7130 else
7131 pp = highlight_init_dark;
7132 for (i = 0; pp[i] != NULL; ++i)
7133 do_highlight((char_u *)pp[i], reset, TRUE);
7134
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007135 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007136 * depend on the number of colors available.
7137 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007138 * to avoid Statement highlighted text disappears.
7139 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007140 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007141 do_highlight((char_u *)(*p_bg == 'l'
7142 ? "Visual cterm=NONE ctermbg=LightGrey"
7143 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007144 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007145 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007146 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7147 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007148 if (*p_bg == 'l')
7149 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7150 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007151
Bram Moolenaar071d4272004-06-13 20:20:40 +00007152#ifdef FEAT_SYN_HL
7153 /*
7154 * If syntax highlighting is enabled load the highlighting for it.
7155 */
7156 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007157 {
7158 static int recursive = 0;
7159
7160 if (recursive >= 5)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007161 emsg(_("E679: recursive loop loading syncolor.vim"));
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007162 else
7163 {
7164 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007165 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007166 --recursive;
7167 }
7168 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007169#endif
7170}
7171
7172/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007173 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007174 * Return OK for success, FAIL for failure.
7175 */
7176 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007177load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007178{
7179 char_u *buf;
7180 int retval = FAIL;
7181 static int recursive = FALSE;
7182
7183 /* When being called recursively, this is probably because setting
7184 * 'background' caused the highlighting to be reloaded. This means it is
7185 * working, thus we should return OK. */
7186 if (recursive)
7187 return OK;
7188
7189 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007190 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007191 if (buf != NULL)
7192 {
Bram Moolenaar60a68362018-04-30 15:40:48 +02007193 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
7194 curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007195 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007196 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007197 vim_free(buf);
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007198 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007199 }
7200 recursive = FALSE;
7201
7202 return retval;
7203}
7204
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007205static char *(color_names[28]) = {
7206 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7207 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7208 "Gray", "Grey", "LightGray", "LightGrey",
7209 "DarkGray", "DarkGrey",
7210 "Blue", "LightBlue", "Green", "LightGreen",
7211 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7212 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7213 /* indices:
7214 * 0, 1, 2, 3,
7215 * 4, 5, 6, 7,
7216 * 8, 9, 10, 11,
7217 * 12, 13,
7218 * 14, 15, 16, 17,
7219 * 18, 19, 20, 21, 22,
7220 * 23, 24, 25, 26, 27 */
7221static int color_numbers_16[28] = {0, 1, 2, 3,
7222 4, 5, 6, 6,
7223 7, 7, 7, 7,
7224 8, 8,
7225 9, 9, 10, 10,
7226 11, 11, 12, 12, 13,
7227 13, 14, 14, 15, -1};
7228/* for xterm with 88 colors... */
7229static int color_numbers_88[28] = {0, 4, 2, 6,
7230 1, 5, 32, 72,
7231 84, 84, 7, 7,
7232 82, 82,
7233 12, 43, 10, 61,
7234 14, 63, 9, 74, 13,
7235 75, 11, 78, 15, -1};
7236/* for xterm with 256 colors... */
7237static int color_numbers_256[28] = {0, 4, 2, 6,
7238 1, 5, 130, 130,
7239 248, 248, 7, 7,
7240 242, 242,
7241 12, 81, 10, 121,
7242 14, 159, 9, 224, 13,
7243 225, 11, 229, 15, -1};
7244/* for terminals with less than 16 colors... */
7245static int color_numbers_8[28] = {0, 4, 2, 6,
7246 1, 5, 3, 3,
7247 7, 7, 7, 7,
7248 0+8, 0+8,
7249 4+8, 4+8, 2+8, 2+8,
7250 6+8, 6+8, 1+8, 1+8, 5+8,
7251 5+8, 3+8, 3+8, 7+8, -1};
7252
7253/*
7254 * Lookup the "cterm" value to be used for color with index "idx" in
7255 * color_names[].
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007256 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
7257 * colors, otherwise it will be unchanged.
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007258 */
7259 int
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007260lookup_color(int idx, int foreground, int *boldp)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007261{
7262 int color = color_numbers_16[idx];
7263 char_u *p;
7264
7265 /* Use the _16 table to check if it's a valid color name. */
7266 if (color < 0)
7267 return -1;
7268
7269 if (t_colors == 8)
7270 {
7271 /* t_Co is 8: use the 8 colors table */
7272#if defined(__QNXNTO__)
7273 color = color_numbers_8_qansi[idx];
7274#else
7275 color = color_numbers_8[idx];
7276#endif
7277 if (foreground)
7278 {
7279 /* set/reset bold attribute to get light foreground
7280 * colors (on some terminals, e.g. "linux") */
7281 if (color & 8)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007282 *boldp = TRUE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007283 else
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007284 *boldp = FALSE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007285 }
7286 color &= 7; /* truncate to 8 colors */
7287 }
7288 else if (t_colors == 16 || t_colors == 88
7289 || t_colors >= 256)
7290 {
7291 /*
7292 * Guess: if the termcap entry ends in 'm', it is
7293 * probably an xterm-like terminal. Use the changed
7294 * order for colors.
7295 */
7296 if (*T_CAF != NUL)
7297 p = T_CAF;
7298 else
7299 p = T_CSF;
7300 if (*p != NUL && (t_colors > 256
7301 || *(p + STRLEN(p) - 1) == 'm'))
7302 {
7303 if (t_colors == 88)
7304 color = color_numbers_88[idx];
7305 else if (t_colors >= 256)
7306 color = color_numbers_256[idx];
7307 else
7308 color = color_numbers_8[idx];
7309 }
Bram Moolenaarc9026092017-10-04 19:35:02 +02007310#ifdef FEAT_TERMRESPONSE
Bram Moolenaara0a6f272017-10-04 18:04:16 +02007311 if (t_colors >= 256 && color == 15 && is_mac_terminal)
7312 /* Terminal.app has a bug: 15 is light grey. Use white
7313 * from the color cube instead. */
7314 color = 231;
Bram Moolenaarc9026092017-10-04 19:35:02 +02007315#endif
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007316 }
7317 return color;
7318}
7319
Bram Moolenaar071d4272004-06-13 20:20:40 +00007320/*
7321 * Handle the ":highlight .." command.
7322 * When using ":hi clear" this is called recursively for each group with
7323 * "forceit" and "init" both TRUE.
7324 */
7325 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007326do_highlight(
7327 char_u *line,
7328 int forceit,
7329 int init) /* TRUE when called for initializing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007330{
7331 char_u *name_end;
7332 char_u *p;
7333 char_u *linep;
7334 char_u *key_start;
7335 char_u *arg_start;
7336 char_u *key = NULL, *arg = NULL;
7337 long i;
7338 int off;
7339 int len;
7340 int attr;
7341 int id;
7342 int idx;
Bram Moolenaar99433292017-09-08 12:37:47 +02007343 struct hl_group item_before;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007344 int did_change = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007345 int dodefault = FALSE;
7346 int doclear = FALSE;
7347 int dolink = FALSE;
7348 int error = FALSE;
7349 int color;
7350 int is_normal_group = FALSE; /* "Normal" group */
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01007351#ifdef FEAT_TERMINAL
7352 int is_terminal_group = FALSE; /* "Terminal" group */
7353#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007354#ifdef FEAT_GUI_X11
7355 int is_menu_group = FALSE; /* "Menu" group */
7356 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7357 int is_tooltip_group = FALSE; /* "Tooltip" group */
7358 int do_colors = FALSE; /* need to update colors? */
7359#else
7360# define is_menu_group 0
7361# define is_tooltip_group 0
7362#endif
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007363#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
7364 int did_highlight_changed = FALSE;
7365#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007366
7367 /*
7368 * If no argument, list current highlighting.
7369 */
7370 if (ends_excmd(*line))
7371 {
7372 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7373 /* TODO: only call when the group has attributes set */
7374 highlight_list_one((int)i);
7375 return;
7376 }
7377
7378 /*
7379 * Isolate the name.
7380 */
7381 name_end = skiptowhite(line);
7382 linep = skipwhite(name_end);
7383
7384 /*
7385 * Check for "default" argument.
7386 */
7387 if (STRNCMP(line, "default", name_end - line) == 0)
7388 {
7389 dodefault = TRUE;
7390 line = linep;
7391 name_end = skiptowhite(line);
7392 linep = skipwhite(name_end);
7393 }
7394
7395 /*
7396 * Check for "clear" or "link" argument.
7397 */
7398 if (STRNCMP(line, "clear", name_end - line) == 0)
7399 doclear = TRUE;
7400 if (STRNCMP(line, "link", name_end - line) == 0)
7401 dolink = TRUE;
7402
7403 /*
7404 * ":highlight {group-name}": list highlighting for one group.
7405 */
7406 if (!doclear && !dolink && ends_excmd(*linep))
7407 {
7408 id = syn_namen2id(line, (int)(name_end - line));
7409 if (id == 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007410 semsg(_("E411: highlight group not found: %s"), line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007411 else
7412 highlight_list_one(id);
7413 return;
7414 }
7415
7416 /*
7417 * Handle ":highlight link {from} {to}" command.
7418 */
7419 if (dolink)
7420 {
7421 char_u *from_start = linep;
7422 char_u *from_end;
7423 char_u *to_start;
7424 char_u *to_end;
7425 int from_id;
7426 int to_id;
7427
7428 from_end = skiptowhite(from_start);
7429 to_start = skipwhite(from_end);
7430 to_end = skiptowhite(to_start);
7431
7432 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7433 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007434 semsg(_("E412: Not enough arguments: \":highlight link %s\""),
Bram Moolenaar071d4272004-06-13 20:20:40 +00007435 from_start);
7436 return;
7437 }
7438
7439 if (!ends_excmd(*skipwhite(to_end)))
7440 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007441 semsg(_("E413: Too many arguments: \":highlight link %s\""), from_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007442 return;
7443 }
7444
7445 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7446 if (STRNCMP(to_start, "NONE", 4) == 0)
7447 to_id = 0;
7448 else
7449 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7450
Bram Moolenaar414168d2017-09-10 15:21:55 +02007451 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007452 {
7453 /*
7454 * Don't allow a link when there already is some highlighting
7455 * for the group, unless '!' is used
7456 */
7457 if (to_id > 0 && !forceit && !init
7458 && hl_has_settings(from_id - 1, dodefault))
7459 {
7460 if (sourcing_name == NULL && !dodefault)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007461 emsg(_("E414: group has settings, highlight link ignored"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007462 }
Bram Moolenaar414168d2017-09-10 15:21:55 +02007463 else if (HL_TABLE()[from_id - 1].sg_link != to_id
Bram Moolenaar99433292017-09-08 12:37:47 +02007464#ifdef FEAT_EVAL
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02007465 || HL_TABLE()[from_id - 1].sg_script_ctx.sc_sid
7466 != current_sctx.sc_sid
Bram Moolenaar99433292017-09-08 12:37:47 +02007467#endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007468 || HL_TABLE()[from_id - 1].sg_cleared)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007469 {
7470 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007471 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7472 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007473#ifdef FEAT_EVAL
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02007474 HL_TABLE()[from_id - 1].sg_script_ctx = current_sctx;
7475 HL_TABLE()[from_id - 1].sg_script_ctx.sc_lnum += sourcing_lnum;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007476#endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007477 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007478 redraw_all_later(SOME_VALID);
Bram Moolenaar99433292017-09-08 12:37:47 +02007479
7480 /* Only call highlight_changed() once after multiple changes. */
7481 need_highlight_changed = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007482 }
7483 }
7484
Bram Moolenaar071d4272004-06-13 20:20:40 +00007485 return;
7486 }
7487
7488 if (doclear)
7489 {
7490 /*
7491 * ":highlight clear [group]" command.
7492 */
7493 line = linep;
7494 if (ends_excmd(*line))
7495 {
7496#ifdef FEAT_GUI
7497 /* First, we do not destroy the old values, but allocate the new
7498 * ones and update the display. THEN we destroy the old values.
7499 * If we destroy the old values first, then the old values
7500 * (such as GuiFont's or GuiFontset's) will still be displayed but
7501 * invalid because they were free'd.
7502 */
7503 if (gui.in_use)
7504 {
7505# ifdef FEAT_BEVAL_TIP
7506 gui_init_tooltip_font();
7507# endif
7508# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7509 gui_init_menu_font();
7510# endif
7511 }
7512# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7513 gui_mch_def_colors();
7514# endif
7515# ifdef FEAT_GUI_X11
7516# ifdef FEAT_MENU
7517
7518 /* This only needs to be done when there is no Menu highlight
7519 * group defined by default, which IS currently the case.
7520 */
7521 gui_mch_new_menu_colors();
7522# endif
7523 if (gui.in_use)
7524 {
7525 gui_new_scrollbar_colors();
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01007526# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007527 gui_mch_new_tooltip_colors();
7528# endif
7529# ifdef FEAT_MENU
7530 gui_mch_new_menu_font();
7531# endif
7532 }
7533# endif
7534
7535 /* Ok, we're done allocating the new default graphics items.
7536 * The screen should already be refreshed at this point.
7537 * It is now Ok to clear out the old data.
7538 */
7539#endif
7540#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007541 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007542#endif
7543 restore_cterm_colors();
7544
7545 /*
7546 * Clear all default highlight groups and load the defaults.
7547 */
7548 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7549 highlight_clear(idx);
7550 init_highlight(TRUE, TRUE);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007551#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007552 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007553 highlight_gui_started();
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007554 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007555#endif
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007556 highlight_changed();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007557 redraw_later_clear();
7558 return;
7559 }
7560 name_end = skiptowhite(line);
7561 linep = skipwhite(name_end);
7562 }
7563
7564 /*
7565 * Find the group name in the table. If it does not exist yet, add it.
7566 */
7567 id = syn_check_group(line, (int)(name_end - line));
7568 if (id == 0) /* failed (out of memory) */
7569 return;
7570 idx = id - 1; /* index is ID minus one */
7571
7572 /* Return if "default" was used and the group already has settings. */
7573 if (dodefault && hl_has_settings(idx, TRUE))
7574 return;
7575
Bram Moolenaar99433292017-09-08 12:37:47 +02007576 /* Make a copy so we can check if any attribute actually changed. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007577 item_before = HL_TABLE()[idx];
Bram Moolenaar99433292017-09-08 12:37:47 +02007578
Bram Moolenaar414168d2017-09-10 15:21:55 +02007579 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007580 is_normal_group = TRUE;
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01007581#ifdef FEAT_TERMINAL
7582 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TERMINAL") == 0)
7583 is_terminal_group = TRUE;
7584#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007585#ifdef FEAT_GUI_X11
Bram Moolenaar414168d2017-09-10 15:21:55 +02007586 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007587 is_menu_group = TRUE;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007588 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007589 is_scrollbar_group = TRUE;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007590 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007591 is_tooltip_group = TRUE;
7592#endif
7593
7594 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7595 if (doclear || (forceit && init))
7596 {
7597 highlight_clear(idx);
7598 if (!doclear)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007599 HL_TABLE()[idx].sg_set = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007600 }
7601
7602 if (!doclear)
7603 while (!ends_excmd(*linep))
7604 {
7605 key_start = linep;
7606 if (*linep == '=')
7607 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007608 semsg(_("E415: unexpected equal sign: %s"), key_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007609 error = TRUE;
7610 break;
7611 }
7612
7613 /*
7614 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7615 * "guibg").
7616 */
Bram Moolenaar1c465442017-03-12 20:10:05 +01007617 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00007618 ++linep;
7619 vim_free(key);
7620 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7621 if (key == NULL)
7622 {
7623 error = TRUE;
7624 break;
7625 }
7626 linep = skipwhite(linep);
7627
7628 if (STRCMP(key, "NONE") == 0)
7629 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007630 if (!init || HL_TABLE()[idx].sg_set == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007631 {
7632 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007633 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007634 highlight_clear(idx);
7635 }
7636 continue;
7637 }
7638
7639 /*
7640 * Check for the equal sign.
7641 */
7642 if (*linep != '=')
7643 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007644 semsg(_("E416: missing equal sign: %s"), key_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007645 error = TRUE;
7646 break;
7647 }
7648 ++linep;
7649
7650 /*
7651 * Isolate the argument.
7652 */
7653 linep = skipwhite(linep);
7654 if (*linep == '\'') /* guifg='color name' */
7655 {
7656 arg_start = ++linep;
7657 linep = vim_strchr(linep, '\'');
7658 if (linep == NULL)
7659 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007660 semsg(_(e_invarg2), key_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007661 error = TRUE;
7662 break;
7663 }
7664 }
7665 else
7666 {
7667 arg_start = linep;
7668 linep = skiptowhite(linep);
7669 }
7670 if (linep == arg_start)
7671 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007672 semsg(_("E417: missing argument: %s"), key_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007673 error = TRUE;
7674 break;
7675 }
7676 vim_free(arg);
7677 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7678 if (arg == NULL)
7679 {
7680 error = TRUE;
7681 break;
7682 }
7683 if (*linep == '\'')
7684 ++linep;
7685
7686 /*
7687 * Store the argument.
7688 */
7689 if ( STRCMP(key, "TERM") == 0
7690 || STRCMP(key, "CTERM") == 0
7691 || STRCMP(key, "GUI") == 0)
7692 {
7693 attr = 0;
7694 off = 0;
7695 while (arg[off] != NUL)
7696 {
7697 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7698 {
7699 len = (int)STRLEN(hl_name_table[i]);
7700 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7701 {
7702 attr |= hl_attr_table[i];
7703 off += len;
7704 break;
7705 }
7706 }
7707 if (i < 0)
7708 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007709 semsg(_("E418: Illegal value: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007710 error = TRUE;
7711 break;
7712 }
7713 if (arg[off] == ',') /* another one follows */
7714 ++off;
7715 }
7716 if (error)
7717 break;
7718 if (*key == 'T')
7719 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007720 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
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_TERM;
7724 HL_TABLE()[idx].sg_term = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007725 }
7726 }
7727 else if (*key == 'C')
7728 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007729 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007730 {
7731 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007732 HL_TABLE()[idx].sg_set |= SG_CTERM;
7733 HL_TABLE()[idx].sg_cterm = attr;
7734 HL_TABLE()[idx].sg_cterm_bold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007735 }
7736 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007737#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007738 else
7739 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007740 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007741 {
7742 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007743 HL_TABLE()[idx].sg_set |= SG_GUI;
7744 HL_TABLE()[idx].sg_gui = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007745 }
7746 }
7747#endif
7748 }
7749 else if (STRCMP(key, "FONT") == 0)
7750 {
7751 /* in non-GUI fonts are simply ignored */
7752#ifdef FEAT_GUI
Bram Moolenaar414168d2017-09-10 15:21:55 +02007753 if (HL_TABLE()[idx].sg_font_name != NULL
7754 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
Bram Moolenaar99433292017-09-08 12:37:47 +02007755 {
7756 /* Font name didn't change, ignore. */
7757 }
7758 else if (!gui.shell_created)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007759 {
7760 /* GUI not started yet, always accept the name. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007761 vim_free(HL_TABLE()[idx].sg_font_name);
7762 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007763 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007764 }
7765 else
7766 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007767 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007768# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007769 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007770# endif
7771 /* First, save the current font/fontset.
7772 * Then try to allocate the font/fontset.
Bram Moolenaar414168d2017-09-10 15:21:55 +02007773 * If the allocation fails, HL_TABLE()[idx].sg_font OR
Bram Moolenaar071d4272004-06-13 20:20:40 +00007774 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7775 */
7776
Bram Moolenaar414168d2017-09-10 15:21:55 +02007777 HL_TABLE()[idx].sg_font = NOFONT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007778# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007779 HL_TABLE()[idx].sg_fontset = NOFONTSET;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007780# endif
7781 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007782 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007783
7784# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007785 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007786 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007787 /* New fontset was accepted. Free the old one, if there
7788 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007789 gui_mch_free_fontset(temp_sg_fontset);
Bram Moolenaar414168d2017-09-10 15:21:55 +02007790 vim_free(HL_TABLE()[idx].sg_font_name);
7791 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007792 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007793 }
7794 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007795 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007796# endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007797 if (HL_TABLE()[idx].sg_font != NOFONT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007798 {
7799 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007800 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007801 gui_mch_free_font(temp_sg_font);
Bram Moolenaar414168d2017-09-10 15:21:55 +02007802 vim_free(HL_TABLE()[idx].sg_font_name);
7803 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007804 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007805 }
7806 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007807 HL_TABLE()[idx].sg_font = temp_sg_font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007808 }
7809#endif
7810 }
7811 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7812 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007813 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007814 {
7815 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007816 HL_TABLE()[idx].sg_set |= SG_CTERM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007817
7818 /* When setting the foreground color, and previously the "bold"
7819 * flag was set for a light color, reset it now */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007820 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007821 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007822 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7823 HL_TABLE()[idx].sg_cterm_bold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007824 }
7825
7826 if (VIM_ISDIGIT(*arg))
7827 color = atoi((char *)arg);
7828 else if (STRICMP(arg, "fg") == 0)
7829 {
7830 if (cterm_normal_fg_color)
7831 color = cterm_normal_fg_color - 1;
7832 else
7833 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007834 emsg(_("E419: FG color unknown"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007835 error = TRUE;
7836 break;
7837 }
7838 }
7839 else if (STRICMP(arg, "bg") == 0)
7840 {
7841 if (cterm_normal_bg_color > 0)
7842 color = cterm_normal_bg_color - 1;
7843 else
7844 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007845 emsg(_("E420: BG color unknown"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007846 error = TRUE;
7847 break;
7848 }
7849 }
7850 else
7851 {
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007852 int bold = MAYBE;
7853
Bram Moolenaar071d4272004-06-13 20:20:40 +00007854#if defined(__QNXNTO__)
7855 static int *color_numbers_8_qansi = color_numbers_8;
7856 /* On qnx, the 8 & 16 color arrays are the same */
7857 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7858 color_numbers_8_qansi = color_numbers_16;
7859#endif
7860
7861 /* reduce calls to STRICMP a bit, it can be slow */
7862 off = TOUPPER_ASC(*arg);
7863 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7864 if (off == color_names[i][0]
7865 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7866 break;
7867 if (i < 0)
7868 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007869 semsg(_("E421: Color name or number not recognized: %s"), key_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007870 error = TRUE;
7871 break;
7872 }
7873
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007874 color = lookup_color(i, key[5] == 'F', &bold);
7875
7876 /* set/reset bold attribute to get light foreground
7877 * colors (on some terminals, e.g. "linux") */
7878 if (bold == TRUE)
7879 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007880 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7881 HL_TABLE()[idx].sg_cterm_bold = TRUE;
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007882 }
7883 else if (bold == FALSE)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007884 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007885 }
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007886
Bram Moolenaarccbab932010-05-13 15:40:30 +02007887 /* Add one to the argument, to avoid zero. Zero is used for
7888 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007889 if (key[5] == 'F')
7890 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007891 HL_TABLE()[idx].sg_cterm_fg = color + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007892 if (is_normal_group)
7893 {
7894 cterm_normal_fg_color = color + 1;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007895 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007896#ifdef FEAT_GUI
7897 /* Don't do this if the GUI is used. */
7898 if (!gui.in_use && !gui.starting)
7899#endif
7900 {
7901 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007902 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007903 term_fg_color(color);
7904 }
7905 }
7906 }
7907 else
7908 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007909 HL_TABLE()[idx].sg_cterm_bg = color + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007910 if (is_normal_group)
7911 {
7912 cterm_normal_bg_color = color + 1;
7913#ifdef FEAT_GUI
7914 /* Don't mess with 'background' if the GUI is used. */
7915 if (!gui.in_use && !gui.starting)
7916#endif
7917 {
7918 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007919 if (color >= 0)
7920 {
Bram Moolenaar1615b362017-06-04 21:06:09 +02007921 int dark = -1;
7922
Bram Moolenaarccbab932010-05-13 15:40:30 +02007923 if (termcap_active)
7924 term_bg_color(color);
7925 if (t_colors < 16)
Bram Moolenaar1615b362017-06-04 21:06:09 +02007926 dark = (color == 0 || color == 4);
7927 /* Limit the heuristic to the standard 16 colors */
7928 else if (color < 16)
7929 dark = (color < 7 || color == 8);
Bram Moolenaarccbab932010-05-13 15:40:30 +02007930 /* Set the 'background' option if the value is
7931 * wrong. */
Bram Moolenaar1615b362017-06-04 21:06:09 +02007932 if (dark != -1
7933 && dark != (*p_bg == 'd')
7934 && !option_was_set((char_u *)"bg"))
7935 {
Bram Moolenaarccbab932010-05-13 15:40:30 +02007936 set_option_value((char_u *)"bg", 0L,
Bram Moolenaar1615b362017-06-04 21:06:09 +02007937 (char_u *)(dark ? "dark" : "light"), 0);
7938 reset_option_was_set((char_u *)"bg");
7939 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007940 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007941 }
7942 }
7943 }
7944 }
7945 }
7946 else if (STRCMP(key, "GUIFG") == 0)
7947 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02007948#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02007949 char_u **namep = &HL_TABLE()[idx].sg_gui_fg_name;
7950
Bram Moolenaar414168d2017-09-10 15:21:55 +02007951 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007952 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007953 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007954 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007955
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007956# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007957 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007958 i = color_name2handle(arg);
Bram Moolenaar63122562016-04-30 12:28:15 +02007959 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007960 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007961 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007962# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02007963 if (*namep == NULL || STRCMP(*namep, arg) != 0)
7964 {
7965 vim_free(*namep);
7966 if (STRCMP(arg, "NONE") != 0)
7967 *namep = vim_strsave(arg);
7968 else
7969 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007970 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02007971 }
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007972# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007973# ifdef FEAT_GUI_X11
Bram Moolenaar452030e2017-09-25 22:57:27 +02007974 if (is_menu_group && gui.menu_fg_pixel != i)
7975 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007976 gui.menu_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02007977 do_colors = TRUE;
7978 }
7979 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
7980 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007981 gui.scroll_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02007982 do_colors = TRUE;
7983 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01007984# ifdef FEAT_BEVAL_GUI
Bram Moolenaar452030e2017-09-25 22:57:27 +02007985 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
7986 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007987 gui.tooltip_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02007988 do_colors = TRUE;
7989 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007990# endif
Bram Moolenaar61623362010-07-14 22:04:22 +02007991# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007992 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007993# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007994 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007995#endif
7996 }
7997 else if (STRCMP(key, "GUIBG") == 0)
7998 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02007999#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02008000 char_u **namep = &HL_TABLE()[idx].sg_gui_bg_name;
8001
Bram Moolenaar414168d2017-09-10 15:21:55 +02008002 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008003 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008004 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008005 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008006
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008007# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008008 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008009 i = color_name2handle(arg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008010 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008011 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008012 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008013# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02008014 if (*namep == NULL || STRCMP(*namep, arg) != 0)
8015 {
8016 vim_free(*namep);
8017 if (STRCMP(arg, "NONE") != 0)
8018 *namep = vim_strsave(arg);
8019 else
8020 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008021 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008022 }
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008023# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008024# ifdef FEAT_GUI_X11
Bram Moolenaar452030e2017-09-25 22:57:27 +02008025 if (is_menu_group && gui.menu_bg_pixel != i)
8026 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008027 gui.menu_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008028 do_colors = TRUE;
8029 }
8030 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
8031 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008032 gui.scroll_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008033 do_colors = TRUE;
8034 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008035# ifdef FEAT_BEVAL_GUI
Bram Moolenaar452030e2017-09-25 22:57:27 +02008036 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
8037 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008038 gui.tooltip_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008039 do_colors = TRUE;
8040 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008041# endif
Bram Moolenaar61623362010-07-14 22:04:22 +02008042# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008043 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008044# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008045 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008046#endif
8047 }
8048 else if (STRCMP(key, "GUISP") == 0)
8049 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02008050#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02008051 char_u **namep = &HL_TABLE()[idx].sg_gui_sp_name;
8052
Bram Moolenaar414168d2017-09-10 15:21:55 +02008053 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008054 {
8055 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008056 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008057
Bram Moolenaar61623362010-07-14 22:04:22 +02008058# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008059 i = color_name2handle(arg);
8060 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
8061 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008062 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008063# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02008064 if (*namep == NULL || STRCMP(*namep, arg) != 0)
8065 {
8066 vim_free(*namep);
8067 if (STRCMP(arg, "NONE") != 0)
8068 *namep = vim_strsave(arg);
8069 else
8070 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008071 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008072 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008073# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008074 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008075# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008076 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008077#endif
8078 }
8079 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
8080 {
8081 char_u buf[100];
8082 char_u *tname;
8083
8084 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008085 HL_TABLE()[idx].sg_set |= SG_TERM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008086
8087 /*
8088 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008089 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008090 */
8091 if (STRNCMP(arg, "t_", 2) == 0)
8092 {
8093 off = 0;
8094 buf[0] = 0;
8095 while (arg[off] != NUL)
8096 {
8097 /* Isolate one termcap name */
8098 for (len = 0; arg[off + len] &&
8099 arg[off + len] != ','; ++len)
8100 ;
8101 tname = vim_strnsave(arg + off, len);
8102 if (tname == NULL) /* out of memory */
8103 {
8104 error = TRUE;
8105 break;
8106 }
8107 /* lookup the escape sequence for the item */
8108 p = get_term_code(tname);
8109 vim_free(tname);
8110 if (p == NULL) /* ignore non-existing things */
8111 p = (char_u *)"";
8112
8113 /* Append it to the already found stuff */
8114 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
8115 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01008116 semsg(_("E422: terminal code too long: %s"), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008117 error = TRUE;
8118 break;
8119 }
8120 STRCAT(buf, p);
8121
8122 /* Advance to the next item */
8123 off += len;
8124 if (arg[off] == ',') /* another one follows */
8125 ++off;
8126 }
8127 }
8128 else
8129 {
8130 /*
8131 * Copy characters from arg[] to buf[], translating <> codes.
8132 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008133 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00008134 {
Bram Moolenaar35a4cfa2016-08-14 16:07:48 +02008135 len = trans_special(&p, buf + off, FALSE, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008136 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008137 off += len;
8138 else /* copy as normal char */
8139 buf[off++] = *p++;
8140 }
8141 buf[off] = NUL;
8142 }
8143 if (error)
8144 break;
8145
8146 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
8147 p = NULL;
8148 else
8149 p = vim_strsave(buf);
8150 if (key[2] == 'A')
8151 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008152 vim_free(HL_TABLE()[idx].sg_start);
8153 HL_TABLE()[idx].sg_start = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008154 }
8155 else
8156 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008157 vim_free(HL_TABLE()[idx].sg_stop);
8158 HL_TABLE()[idx].sg_stop = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008159 }
8160 }
8161 else
8162 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01008163 semsg(_("E423: Illegal argument: %s"), key_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008164 error = TRUE;
8165 break;
8166 }
Bram Moolenaar414168d2017-09-10 15:21:55 +02008167 HL_TABLE()[idx].sg_cleared = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008168
8169 /*
8170 * When highlighting has been given for a group, don't link it.
8171 */
Bram Moolenaar414168d2017-09-10 15:21:55 +02008172 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8173 HL_TABLE()[idx].sg_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008174
8175 /*
8176 * Continue with next argument.
8177 */
8178 linep = skipwhite(linep);
8179 }
8180
8181 /*
8182 * If there is an error, and it's a new entry, remove it from the table.
8183 */
8184 if (error && idx == highlight_ga.ga_len)
8185 syn_unadd_group();
8186 else
8187 {
8188 if (is_normal_group)
8189 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008190 HL_TABLE()[idx].sg_term_attr = 0;
8191 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008192#ifdef FEAT_GUI
Bram Moolenaar414168d2017-09-10 15:21:55 +02008193 HL_TABLE()[idx].sg_gui_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008194 /*
8195 * Need to update all groups, because they might be using "bg"
8196 * and/or "fg", which have been changed now.
8197 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008198#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008199#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008200 if (USE_24BIT)
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008201 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008202 highlight_gui_started();
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008203 did_highlight_changed = TRUE;
8204 redraw_all_later(NOT_VALID);
8205 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008206#endif
8207 }
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01008208#ifdef FEAT_TERMINAL
8209 else if (is_terminal_group)
8210 set_terminal_default_colors(
8211 HL_TABLE()[idx].sg_cterm_fg, HL_TABLE()[idx].sg_cterm_bg);
8212#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008213#ifdef FEAT_GUI_X11
8214# ifdef FEAT_MENU
8215 else if (is_menu_group)
8216 {
8217 if (gui.in_use && do_colors)
8218 gui_mch_new_menu_colors();
8219 }
8220# endif
8221 else if (is_scrollbar_group)
8222 {
8223 if (gui.in_use && do_colors)
8224 gui_new_scrollbar_colors();
8225 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008226# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00008227 else if (is_tooltip_group)
8228 {
8229 if (gui.in_use && do_colors)
8230 gui_mch_new_tooltip_colors();
8231 }
8232# endif
8233#endif
8234 else
8235 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008236#ifdef FEAT_EVAL
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02008237 HL_TABLE()[idx].sg_script_ctx = current_sctx;
8238 HL_TABLE()[idx].sg_script_ctx.sc_lnum += sourcing_lnum;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008239#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008240 }
Bram Moolenaar99433292017-09-08 12:37:47 +02008241
Bram Moolenaar071d4272004-06-13 20:20:40 +00008242 vim_free(key);
8243 vim_free(arg);
8244
Bram Moolenaar99433292017-09-08 12:37:47 +02008245 /* Only call highlight_changed() once, after a sequence of highlight
8246 * commands, and only if an attribute actually changed. */
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008247 if ((did_change
8248 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008249#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
8250 && !did_highlight_changed
8251#endif
8252 )
Bram Moolenaar99433292017-09-08 12:37:47 +02008253 {
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008254 /* Do not trigger a redraw when highlighting is changed while
8255 * redrawing. This may happen when evaluating 'statusline' changes the
8256 * StatusLine group. */
8257 if (!updating_screen)
8258 redraw_all_later(NOT_VALID);
Bram Moolenaar99433292017-09-08 12:37:47 +02008259 need_highlight_changed = TRUE;
8260 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008261}
8262
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008263#if defined(EXITFREE) || defined(PROTO)
8264 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008265free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008266{
8267 int i;
8268
8269 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008270 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008271 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008272 vim_free(HL_TABLE()[i].sg_name);
8273 vim_free(HL_TABLE()[i].sg_name_u);
8274 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008275 ga_clear(&highlight_ga);
8276}
8277#endif
8278
Bram Moolenaar071d4272004-06-13 20:20:40 +00008279/*
8280 * Reset the cterm colors to what they were before Vim was started, if
8281 * possible. Otherwise reset them to zero.
8282 */
8283 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008284restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008285{
Bram Moolenaar48e330a2016-02-23 14:53:34 +01008286#if defined(WIN3264) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008287 /* Since t_me has been set, this probably means that the user
8288 * wants to use this as default colors. Need to reset default
8289 * background/foreground colors. */
8290 mch_set_normal_colors();
8291#else
8292 cterm_normal_fg_color = 0;
8293 cterm_normal_fg_bold = 0;
8294 cterm_normal_bg_color = 0;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008295# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008296 cterm_normal_fg_gui_color = INVALCOLOR;
8297 cterm_normal_bg_gui_color = INVALCOLOR;
8298# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008299#endif
8300}
8301
8302/*
8303 * Return TRUE if highlight group "idx" has any settings.
8304 * When "check_link" is TRUE also check for an existing link.
8305 */
8306 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008307hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008308{
8309 return ( HL_TABLE()[idx].sg_term_attr != 0
8310 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008311 || HL_TABLE()[idx].sg_cterm_fg != 0
8312 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008313#ifdef FEAT_GUI
8314 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008315 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8316 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8317 || HL_TABLE()[idx].sg_gui_sp_name != NULL
Bram Moolenaar87748452017-03-12 17:10:33 +01008318 || HL_TABLE()[idx].sg_font_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008319#endif
8320 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8321}
8322
8323/*
8324 * Clear highlighting for one group.
8325 */
8326 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008327highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008328{
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008329 HL_TABLE()[idx].sg_cleared = TRUE;
8330
Bram Moolenaar071d4272004-06-13 20:20:40 +00008331 HL_TABLE()[idx].sg_term = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +01008332 VIM_CLEAR(HL_TABLE()[idx].sg_start);
8333 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008334 HL_TABLE()[idx].sg_term_attr = 0;
8335 HL_TABLE()[idx].sg_cterm = 0;
8336 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8337 HL_TABLE()[idx].sg_cterm_fg = 0;
8338 HL_TABLE()[idx].sg_cterm_bg = 0;
8339 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008340#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008341 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +01008342 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
8343 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
8344 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
Bram Moolenaar61623362010-07-14 22:04:22 +02008345#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008346#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008347 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8348 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008349#endif
8350#ifdef FEAT_GUI
Bram Moolenaar61623362010-07-14 22:04:22 +02008351 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008352 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8353 HL_TABLE()[idx].sg_font = NOFONT;
8354# ifdef FEAT_XFONTSET
8355 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8356 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8357# endif
Bram Moolenaard23a8232018-02-10 18:45:26 +01008358 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008359 HL_TABLE()[idx].sg_gui_attr = 0;
8360#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008361#ifdef FEAT_EVAL
8362 /* Clear the script ID only when there is no link, since that is not
8363 * cleared. */
8364 if (HL_TABLE()[idx].sg_link == 0)
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02008365 {
8366 HL_TABLE()[idx].sg_script_ctx.sc_sid = 0;
8367 HL_TABLE()[idx].sg_script_ctx.sc_lnum = 0;
8368 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008369#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008370}
8371
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008372#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008373/*
8374 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008375 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008376 * "Tooltip" colors.
8377 */
8378 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008379set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008380{
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008381# ifdef FEAT_GUI
8382# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008383 if (gui.in_use)
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008384# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008385 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008386 if (set_group_colors((char_u *)"Normal",
8387 &gui.norm_pixel, &gui.back_pixel,
8388 FALSE, TRUE, FALSE))
8389 {
8390 gui_mch_new_colors();
8391 must_redraw = CLEAR;
8392 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008393# ifdef FEAT_GUI_X11
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008394 if (set_group_colors((char_u *)"Menu",
8395 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8396 TRUE, FALSE, FALSE))
8397 {
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008398# ifdef FEAT_MENU
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008399 gui_mch_new_menu_colors();
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008400# endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008401 must_redraw = CLEAR;
8402 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008403# ifdef FEAT_BEVAL_GUI
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008404 if (set_group_colors((char_u *)"Tooltip",
8405 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8406 FALSE, FALSE, TRUE))
8407 {
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008408# ifdef FEAT_TOOLBAR
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008409 gui_mch_new_tooltip_colors();
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008410# endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008411 must_redraw = CLEAR;
8412 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008413# endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008414 if (set_group_colors((char_u *)"Scrollbar",
8415 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8416 FALSE, FALSE, FALSE))
8417 {
8418 gui_new_scrollbar_colors();
8419 must_redraw = CLEAR;
8420 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008421# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008422 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008423# endif
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008424# ifdef FEAT_TERMGUICOLORS
8425# ifdef FEAT_GUI
8426 else
8427# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008428 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008429 int idx;
8430
8431 idx = syn_name2id((char_u *)"Normal") - 1;
8432 if (idx >= 0)
8433 {
8434 gui_do_one_color(idx, FALSE, FALSE);
8435
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008436 /* If the normal fg or bg color changed a complete redraw is
8437 * required. */
8438 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
8439 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008440 {
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008441 /* if the GUI color is INVALCOLOR then we use the default cterm
8442 * color */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008443 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008444 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
8445 must_redraw = CLEAR;
8446 }
8447 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008448 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008449# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008450}
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008451#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008452
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008453#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008454/*
8455 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8456 */
8457 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008458set_group_colors(
8459 char_u *name,
8460 guicolor_T *fgp,
8461 guicolor_T *bgp,
8462 int do_menu,
8463 int use_norm,
8464 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008465{
8466 int idx;
8467
8468 idx = syn_name2id(name) - 1;
8469 if (idx >= 0)
8470 {
8471 gui_do_one_color(idx, do_menu, do_tooltip);
8472
8473 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8474 *fgp = HL_TABLE()[idx].sg_gui_fg;
8475 else if (use_norm)
8476 *fgp = gui.def_norm_pixel;
8477 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8478 *bgp = HL_TABLE()[idx].sg_gui_bg;
8479 else if (use_norm)
8480 *bgp = gui.def_back_pixel;
8481 return TRUE;
8482 }
8483 return FALSE;
8484}
8485
8486/*
8487 * Get the font of the "Normal" group.
8488 * Returns "" when it's not found or not set.
8489 */
8490 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008491hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008492{
8493 int id;
8494 char_u *s;
8495
8496 id = syn_name2id((char_u *)"Normal");
8497 if (id > 0)
8498 {
8499 s = HL_TABLE()[id - 1].sg_font_name;
8500 if (s != NULL)
8501 return s;
8502 }
8503 return (char_u *)"";
8504}
8505
8506/*
8507 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8508 * actually chosen to be used.
8509 */
8510 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008511hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008512{
8513 int id;
8514
8515 id = syn_name2id((char_u *)"Normal");
8516 if (id > 0)
8517 {
8518 vim_free(HL_TABLE()[id - 1].sg_font_name);
8519 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8520 }
8521}
8522
8523/*
8524 * Set background color for "Normal" group. Called by gui_set_bg_color()
8525 * when the color is known.
8526 */
8527 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008528hl_set_bg_color_name(
8529 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008530{
8531 int id;
8532
8533 if (name != NULL)
8534 {
8535 id = syn_name2id((char_u *)"Normal");
8536 if (id > 0)
8537 {
8538 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8539 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8540 }
8541 }
8542}
8543
8544/*
8545 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8546 * when the color is known.
8547 */
8548 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008549hl_set_fg_color_name(
8550 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008551{
8552 int id;
8553
8554 if (name != NULL)
8555 {
8556 id = syn_name2id((char_u *)"Normal");
8557 if (id > 0)
8558 {
8559 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8560 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8561 }
8562 }
8563}
8564
8565/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008566 * Return the handle for a font name.
8567 * Returns NOFONT when failed.
8568 */
8569 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008570font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008571{
8572 if (STRCMP(name, "NONE") == 0)
8573 return NOFONT;
8574
8575 return gui_mch_get_font(name, TRUE);
8576}
8577
8578# ifdef FEAT_XFONTSET
8579/*
8580 * Return the handle for a fontset name.
8581 * Returns NOFONTSET when failed.
8582 */
8583 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008584fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008585{
8586 if (STRCMP(name, "NONE") == 0)
8587 return NOFONTSET;
8588
8589 return gui_mch_get_fontset(name, TRUE, fixed_width);
8590}
8591# endif
8592
8593/*
8594 * Get the font or fontset for one highlight group.
8595 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008596 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008597hl_do_font(
8598 int idx,
8599 char_u *arg,
8600 int do_normal, /* set normal font */
8601 int do_menu UNUSED, /* set menu font */
8602 int do_tooltip UNUSED, /* set tooltip font */
8603 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008604{
8605# ifdef FEAT_XFONTSET
8606 /* If 'guifontset' is not empty, first try using the name as a
8607 * fontset. If that doesn't work, use it as a font name. */
8608 if (*p_guifontset != NUL
8609# ifdef FONTSET_ALWAYS
8610 || do_menu
8611# endif
8612# ifdef FEAT_BEVAL_TIP
8613 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8614 || do_tooltip
8615# endif
8616 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008617 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008618 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008619 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008620 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8621# ifdef FONTSET_ALWAYS
8622 || do_menu
8623# endif
8624# ifdef FEAT_BEVAL_TIP
8625 || do_tooltip
8626# endif
8627 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008628 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008629 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8630 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008631 /* If it worked and it's the Normal group, use it as the normal
8632 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008633 if (do_normal)
8634 gui_init_font(arg, TRUE);
8635# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8636 if (do_menu)
8637 {
8638# ifdef FONTSET_ALWAYS
8639 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8640# else
8641 /* YIKES! This is a bug waiting to crash the program */
8642 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8643# endif
8644 gui_mch_new_menu_font();
8645 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008646# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00008647 if (do_tooltip)
8648 {
8649 /* The Athena widget set cannot currently handle switching between
8650 * displaying a single font and a fontset.
8651 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008652 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008653 * XFontStruct is used.
8654 */
8655 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8656 gui_mch_new_tooltip_font();
8657 }
8658# endif
8659# endif
8660 }
8661 else
8662# endif
8663 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008664 if (free_font)
8665 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008666 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8667 /* If it worked and it's the Normal group, use it as the
8668 * normal font. Same for the Menu group. */
8669 if (HL_TABLE()[idx].sg_font != NOFONT)
8670 {
8671 if (do_normal)
8672 gui_init_font(arg, FALSE);
8673#ifndef FONTSET_ALWAYS
8674# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8675 if (do_menu)
8676 {
8677 gui.menu_font = HL_TABLE()[idx].sg_font;
8678 gui_mch_new_menu_font();
8679 }
8680# endif
8681#endif
8682 }
8683 }
8684}
8685
8686#endif /* FEAT_GUI */
8687
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008688#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008689/*
8690 * Return the handle for a color name.
8691 * Returns INVALCOLOR when failed.
8692 */
Bram Moolenaar3d9bdfe2017-08-12 22:55:58 +02008693 guicolor_T
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008694color_name2handle(char_u *name)
8695{
8696 if (STRCMP(name, "NONE") == 0)
8697 return INVALCOLOR;
8698
8699 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8700 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008701#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008702 if (gui.in_use)
8703#endif
8704#ifdef FEAT_GUI
8705 return gui.norm_pixel;
8706#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008707#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008708 if (cterm_normal_fg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008709 return cterm_normal_fg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008710 /* Guess that the foreground is black or white. */
8711 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008712#endif
8713 }
8714 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8715 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008716#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008717 if (gui.in_use)
8718#endif
8719#ifdef FEAT_GUI
8720 return gui.back_pixel;
8721#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008722#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008723 if (cterm_normal_bg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008724 return cterm_normal_bg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008725 /* Guess that the background is white or black. */
8726 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008727#endif
8728 }
8729
8730 return GUI_GET_COLOR(name);
8731}
8732#endif
8733
Bram Moolenaar071d4272004-06-13 20:20:40 +00008734/*
8735 * Table with the specifications for an attribute number.
8736 * Note that this table is used by ALL buffers. This is required because the
8737 * GUI can redraw at any time for any buffer.
8738 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008739static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008740
8741#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8742
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008743static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008744
8745#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8746
8747#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008748static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008749
8750#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8751#endif
8752
8753/*
8754 * Return the attr number for a set of colors and font.
8755 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8756 * if the combination is new.
8757 * Return 0 for error (no more room).
8758 */
8759 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008760get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008761{
8762 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008763 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008764 static int recursive = FALSE;
8765
8766 /*
8767 * Init the table, in case it wasn't done yet.
8768 */
8769 table->ga_itemsize = sizeof(attrentry_T);
8770 table->ga_growsize = 7;
8771
8772 /*
8773 * Try to find an entry with the same specifications.
8774 */
8775 for (i = 0; i < table->ga_len; ++i)
8776 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008777 taep = &(((attrentry_T *)table->ga_data)[i]);
8778 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008779 && (
8780#ifdef FEAT_GUI
8781 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008782 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8783 && aep->ae_u.gui.bg_color
8784 == taep->ae_u.gui.bg_color
8785 && aep->ae_u.gui.sp_color
8786 == taep->ae_u.gui.sp_color
8787 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008788# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008789 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008790# endif
8791 ))
8792 ||
8793#endif
8794 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008795 && (aep->ae_u.term.start == NULL)
8796 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008797 && (aep->ae_u.term.start == NULL
8798 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008799 taep->ae_u.term.start) == 0)
8800 && (aep->ae_u.term.stop == NULL)
8801 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008802 && (aep->ae_u.term.stop == NULL
8803 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008804 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008805 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008806 && aep->ae_u.cterm.fg_color
8807 == taep->ae_u.cterm.fg_color
8808 && aep->ae_u.cterm.bg_color
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008809 == taep->ae_u.cterm.bg_color
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008810#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008811 && aep->ae_u.cterm.fg_rgb
8812 == taep->ae_u.cterm.fg_rgb
8813 && aep->ae_u.cterm.bg_rgb
8814 == taep->ae_u.cterm.bg_rgb
8815#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008816 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008817
8818 return i + ATTR_OFF;
8819 }
8820
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008821 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008822 {
8823 /*
8824 * Running out of attribute entries! remove all attributes, and
8825 * compute new ones for all groups.
8826 * When called recursively, we are really out of numbers.
8827 */
8828 if (recursive)
8829 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01008830 emsg(_("E424: Too many different highlighting attributes in use"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008831 return 0;
8832 }
8833 recursive = TRUE;
8834
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008835 clear_hl_tables();
8836
Bram Moolenaar071d4272004-06-13 20:20:40 +00008837 must_redraw = CLEAR;
8838
8839 for (i = 0; i < highlight_ga.ga_len; ++i)
8840 set_hl_attr(i);
8841
8842 recursive = FALSE;
8843 }
8844
8845 /*
8846 * This is a new combination of colors and font, add an entry.
8847 */
8848 if (ga_grow(table, 1) == FAIL)
8849 return 0;
8850
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008851 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8852 vim_memset(taep, 0, sizeof(attrentry_T));
8853 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008854#ifdef FEAT_GUI
8855 if (table == &gui_attr_table)
8856 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008857 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8858 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8859 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8860 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008861# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008862 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008863# endif
8864 }
8865#endif
8866 if (table == &term_attr_table)
8867 {
8868 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008869 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008870 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008871 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008872 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008873 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008874 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008875 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008876 }
8877 else if (table == &cterm_attr_table)
8878 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008879 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8880 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008881#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008882 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
8883 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
8884#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008885 }
8886 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008887 return (table->ga_len - 1 + ATTR_OFF);
8888}
8889
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008890/*
Bram Moolenaareeac6772017-07-23 15:48:37 +02008891 * Get an attribute index for a cterm entry.
8892 * Uses an existing entry when possible or adds one when needed.
8893 */
8894 int
8895get_cterm_attr_idx(int attr, int fg, int bg)
8896{
8897 attrentry_T at_en;
8898
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008899 vim_memset(&at_en, 0, sizeof(attrentry_T));
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008900#ifdef FEAT_TERMGUICOLORS
8901 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
8902 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
8903#endif
Bram Moolenaareeac6772017-07-23 15:48:37 +02008904 at_en.ae_attr = attr;
8905 at_en.ae_u.cterm.fg_color = fg;
8906 at_en.ae_u.cterm.bg_color = bg;
8907 return get_attr_entry(&cterm_attr_table, &at_en);
8908}
8909
Bram Moolenaar065f41c2017-07-23 18:07:56 +02008910#if defined(FEAT_TERMGUICOLORS) || defined(PROTO)
8911/*
8912 * Get an attribute index for a 'termguicolors' entry.
8913 * Uses an existing entry when possible or adds one when needed.
8914 */
8915 int
8916get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8917{
8918 attrentry_T at_en;
8919
8920 vim_memset(&at_en, 0, sizeof(attrentry_T));
8921 at_en.ae_attr = attr;
Bram Moolenaard4fc5772018-02-27 14:39:03 +01008922 if (fg == INVALCOLOR && bg == INVALCOLOR)
8923 {
8924 /* If both GUI colors are not set fall back to the cterm colors. Helps
8925 * if the GUI only has an attribute, such as undercurl. */
8926 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
8927 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
8928 }
8929 else
8930 {
8931 at_en.ae_u.cterm.fg_rgb = fg;
8932 at_en.ae_u.cterm.bg_rgb = bg;
8933 }
Bram Moolenaar065f41c2017-07-23 18:07:56 +02008934 return get_attr_entry(&cterm_attr_table, &at_en);
8935}
8936#endif
8937
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008938#if defined(FEAT_GUI) || defined(PROTO)
8939/*
8940 * Get an attribute index for a cterm entry.
8941 * Uses an existing entry when possible or adds one when needed.
8942 */
8943 int
8944get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8945{
8946 attrentry_T at_en;
8947
8948 vim_memset(&at_en, 0, sizeof(attrentry_T));
8949 at_en.ae_attr = attr;
8950 at_en.ae_u.gui.fg_color = fg;
8951 at_en.ae_u.gui.bg_color = bg;
8952 return get_attr_entry(&gui_attr_table, &at_en);
8953}
8954#endif
8955
Bram Moolenaareeac6772017-07-23 15:48:37 +02008956/*
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008957 * Clear all highlight tables.
8958 */
8959 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008960clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008961{
8962 int i;
8963 attrentry_T *taep;
8964
8965#ifdef FEAT_GUI
8966 ga_clear(&gui_attr_table);
8967#endif
8968 for (i = 0; i < term_attr_table.ga_len; ++i)
8969 {
8970 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8971 vim_free(taep->ae_u.term.start);
8972 vim_free(taep->ae_u.term.stop);
8973 }
8974 ga_clear(&term_attr_table);
8975 ga_clear(&cterm_attr_table);
8976}
8977
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008978#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008979/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008980 * Combine special attributes (e.g., for spelling) with other attributes
8981 * (e.g., for syntax highlighting).
8982 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008983 * This creates a new group when required.
8984 * Since we expect there to be few spelling mistakes we don't cache the
8985 * result.
8986 * Return the resulting attributes.
8987 */
8988 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008989hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008990{
8991 attrentry_T *char_aep = NULL;
8992 attrentry_T *spell_aep;
8993 attrentry_T new_en;
8994
8995 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008996 return prim_attr;
8997 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02008998 return ATTR_COMBINE(char_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008999#ifdef FEAT_GUI
9000 if (gui.in_use)
9001 {
9002 if (char_attr > HL_ALL)
9003 char_aep = syn_gui_attr2entry(char_attr);
9004 if (char_aep != NULL)
9005 new_en = *char_aep;
9006 else
9007 {
9008 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00009009 new_en.ae_u.gui.fg_color = INVALCOLOR;
9010 new_en.ae_u.gui.bg_color = INVALCOLOR;
9011 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00009012 if (char_attr <= HL_ALL)
9013 new_en.ae_attr = char_attr;
9014 }
9015
Bram Moolenaar30abd282005-06-22 22:35:10 +00009016 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009017 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009018 else
9019 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00009020 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009021 if (spell_aep != NULL)
9022 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009023 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
9024 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009025 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
9026 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
9027 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
9028 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
9029 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
9030 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
9031 if (spell_aep->ae_u.gui.font != NOFONT)
9032 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
9033# ifdef FEAT_XFONTSET
9034 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
9035 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
9036# endif
9037 }
9038 }
9039 return get_attr_entry(&gui_attr_table, &new_en);
9040 }
9041#endif
9042
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009043 if (IS_CTERM)
Bram Moolenaar217ad922005-03-20 22:37:15 +00009044 {
9045 if (char_attr > HL_ALL)
9046 char_aep = syn_cterm_attr2entry(char_attr);
9047 if (char_aep != NULL)
9048 new_en = *char_aep;
9049 else
9050 {
9051 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaar0cdb72a2017-01-02 21:37:40 +01009052#ifdef FEAT_TERMGUICOLORS
9053 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
9054 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
9055#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00009056 if (char_attr <= HL_ALL)
9057 new_en.ae_attr = char_attr;
9058 }
9059
Bram Moolenaar30abd282005-06-22 22:35:10 +00009060 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009061 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009062 else
9063 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00009064 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009065 if (spell_aep != NULL)
9066 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009067 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
9068 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009069 if (spell_aep->ae_u.cterm.fg_color > 0)
9070 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
9071 if (spell_aep->ae_u.cterm.bg_color > 0)
9072 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009073#ifdef FEAT_TERMGUICOLORS
Bram Moolenaard4fc5772018-02-27 14:39:03 +01009074 /* If both fg and bg are not set fall back to cterm colors.
9075 * Helps for SpellBad which uses undercurl in the GUI. */
9076 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
9077 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
9078 {
9079 if (spell_aep->ae_u.cterm.fg_color > 0)
9080 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
9081 if (spell_aep->ae_u.cterm.bg_color > 0)
9082 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
9083 }
9084 else
9085 {
9086 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
9087 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
9088 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
9089 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
9090 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009091#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00009092 }
9093 }
9094 return get_attr_entry(&cterm_attr_table, &new_en);
9095 }
9096
9097 if (char_attr > HL_ALL)
9098 char_aep = syn_term_attr2entry(char_attr);
9099 if (char_aep != NULL)
9100 new_en = *char_aep;
9101 else
9102 {
9103 vim_memset(&new_en, 0, sizeof(new_en));
9104 if (char_attr <= HL_ALL)
9105 new_en.ae_attr = char_attr;
9106 }
9107
Bram Moolenaar30abd282005-06-22 22:35:10 +00009108 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009109 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009110 else
9111 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00009112 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009113 if (spell_aep != NULL)
9114 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009115 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009116 if (spell_aep->ae_u.term.start != NULL)
9117 {
9118 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
9119 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
9120 }
9121 }
9122 }
9123 return get_attr_entry(&term_attr_table, &new_en);
9124}
9125#endif
9126
Bram Moolenaar071d4272004-06-13 20:20:40 +00009127#ifdef FEAT_GUI
9128
9129 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009130syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009131{
9132 attr -= ATTR_OFF;
9133 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
9134 return NULL;
9135 return &(GUI_ATTR_ENTRY(attr));
9136}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009137#endif /* FEAT_GUI */
9138
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009139/*
9140 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
9141 * Only to be used when "attr" > HL_ALL.
9142 */
9143 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009144syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009145{
9146 attrentry_T *aep;
9147
9148#ifdef FEAT_GUI
9149 if (gui.in_use)
9150 aep = syn_gui_attr2entry(attr);
9151 else
9152#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009153 if (IS_CTERM)
9154 aep = syn_cterm_attr2entry(attr);
9155 else
9156 aep = syn_term_attr2entry(attr);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009157
9158 if (aep == NULL) /* highlighting not set */
9159 return 0;
9160 return aep->ae_attr;
9161}
9162
9163
Bram Moolenaar071d4272004-06-13 20:20:40 +00009164 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009165syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009166{
9167 attr -= ATTR_OFF;
9168 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
9169 return NULL;
9170 return &(TERM_ATTR_ENTRY(attr));
9171}
9172
9173 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009174syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009175{
9176 attr -= ATTR_OFF;
9177 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
9178 return NULL;
9179 return &(CTERM_ATTR_ENTRY(attr));
9180}
9181
9182#define LIST_ATTR 1
9183#define LIST_STRING 2
9184#define LIST_INT 3
9185
9186 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009187highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009188{
9189 struct hl_group *sgp;
9190 int didh = FALSE;
9191
Bram Moolenaarf86db782018-10-25 13:31:37 +02009192 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
9193
9194 if (message_filtered(sgp->sg_name))
9195 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009196
9197 didh = highlight_list_arg(id, didh, LIST_ATTR,
9198 sgp->sg_term, NULL, "term");
9199 didh = highlight_list_arg(id, didh, LIST_STRING,
9200 0, sgp->sg_start, "start");
9201 didh = highlight_list_arg(id, didh, LIST_STRING,
9202 0, sgp->sg_stop, "stop");
9203
9204 didh = highlight_list_arg(id, didh, LIST_ATTR,
9205 sgp->sg_cterm, NULL, "cterm");
9206 didh = highlight_list_arg(id, didh, LIST_INT,
9207 sgp->sg_cterm_fg, NULL, "ctermfg");
9208 didh = highlight_list_arg(id, didh, LIST_INT,
9209 sgp->sg_cterm_bg, NULL, "ctermbg");
9210
Bram Moolenaar61623362010-07-14 22:04:22 +02009211#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009212 didh = highlight_list_arg(id, didh, LIST_ATTR,
9213 sgp->sg_gui, NULL, "gui");
9214 didh = highlight_list_arg(id, didh, LIST_STRING,
9215 0, sgp->sg_gui_fg_name, "guifg");
9216 didh = highlight_list_arg(id, didh, LIST_STRING,
9217 0, sgp->sg_gui_bg_name, "guibg");
9218 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009219 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02009220#endif
9221#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009222 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00009223 0, sgp->sg_font_name, "font");
9224#endif
9225
Bram Moolenaar661b1822005-07-28 22:36:45 +00009226 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009227 {
9228 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00009229 didh = TRUE;
Bram Moolenaar8820b482017-03-16 17:23:31 +01009230 msg_puts_attr((char_u *)"links to", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009231 msg_putchar(' ');
9232 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
9233 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009234
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009235 if (!didh)
9236 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00009237#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009238 if (p_verbose > 0)
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02009239 last_set_msg(sgp->sg_script_ctx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00009240#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009241}
9242
9243 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009244highlight_list_arg(
9245 int id,
9246 int didh,
9247 int type,
9248 int iarg,
9249 char_u *sarg,
9250 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009251{
9252 char_u buf[100];
9253 char_u *ts;
9254 int i;
9255
Bram Moolenaar661b1822005-07-28 22:36:45 +00009256 if (got_int)
9257 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009258 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
9259 {
9260 ts = buf;
9261 if (type == LIST_INT)
9262 sprintf((char *)buf, "%d", iarg - 1);
9263 else if (type == LIST_STRING)
9264 ts = sarg;
9265 else /* type == LIST_ATTR */
9266 {
9267 buf[0] = NUL;
9268 for (i = 0; hl_attr_table[i] != 0; ++i)
9269 {
9270 if (iarg & hl_attr_table[i])
9271 {
9272 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02009273 vim_strcat(buf, (char_u *)",", 100);
9274 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009275 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
9276 }
9277 }
9278 }
9279
9280 (void)syn_list_header(didh,
9281 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9282 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00009283 if (!got_int)
9284 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009285 if (*name != NUL)
9286 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01009287 MSG_PUTS_ATTR(name, HL_ATTR(HLF_D));
9288 MSG_PUTS_ATTR("=", HL_ATTR(HLF_D));
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009289 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009290 msg_outtrans(ts);
9291 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009292 }
9293 return didh;
9294}
9295
9296#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9297/*
9298 * Return "1" if highlight group "id" has attribute "flag".
9299 * Return NULL otherwise.
9300 */
9301 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009302highlight_has_attr(
9303 int id,
9304 int flag,
9305 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009306{
9307 int attr;
9308
9309 if (id <= 0 || id > highlight_ga.ga_len)
9310 return NULL;
9311
Bram Moolenaar61623362010-07-14 22:04:22 +02009312#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009313 if (modec == 'g')
9314 attr = HL_TABLE()[id - 1].sg_gui;
9315 else
9316#endif
9317 if (modec == 'c')
9318 attr = HL_TABLE()[id - 1].sg_cterm;
9319 else
9320 attr = HL_TABLE()[id - 1].sg_term;
9321
9322 if (attr & flag)
9323 return (char_u *)"1";
9324 return NULL;
9325}
9326#endif
9327
9328#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9329/*
9330 * Return color name of highlight group "id".
9331 */
9332 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009333highlight_color(
9334 int id,
9335 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
9336 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009337{
9338 static char_u name[20];
9339 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009340 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009341 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009342 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009343
9344 if (id <= 0 || id > highlight_ga.ga_len)
9345 return NULL;
9346
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009347 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009348 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009349 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02009350 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009351 font = TRUE;
9352 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009353 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009354 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9355 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009356 if (modec == 'g')
9357 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009358# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009359# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009360 /* return font name */
9361 if (font)
9362 return HL_TABLE()[id - 1].sg_font_name;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009363# endif
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009364
Bram Moolenaar071d4272004-06-13 20:20:40 +00009365 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009366 if ((USE_24BIT) && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009367 {
9368 guicolor_T color;
9369 long_u rgb;
9370 static char_u buf[10];
9371
9372 if (fg)
9373 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009374 else if (sp)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009375# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009376 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009377# else
9378 color = INVALCOLOR;
9379# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009380 else
9381 color = HL_TABLE()[id - 1].sg_gui_bg;
9382 if (color == INVALCOLOR)
9383 return NULL;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009384 rgb = (long_u)GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009385 sprintf((char *)buf, "#%02x%02x%02x",
9386 (unsigned)(rgb >> 16),
9387 (unsigned)(rgb >> 8) & 255,
9388 (unsigned)rgb & 255);
9389 return buf;
9390 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009391# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009392 if (fg)
9393 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009394 if (sp)
9395 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009396 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9397 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009398 if (font || sp)
9399 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009400 if (modec == 'c')
9401 {
9402 if (fg)
9403 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9404 else
9405 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009406 if (n < 0)
9407 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009408 sprintf((char *)name, "%d", n);
9409 return name;
9410 }
9411 /* term doesn't have color */
9412 return NULL;
9413}
9414#endif
9415
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009416#if (defined(FEAT_SYN_HL) \
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009417 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009418 && defined(FEAT_PRINTER)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009419/*
9420 * Return color name of highlight group "id" as RGB value.
9421 */
9422 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009423highlight_gui_color_rgb(
9424 int id,
9425 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009426{
9427 guicolor_T color;
9428
9429 if (id <= 0 || id > highlight_ga.ga_len)
9430 return 0L;
9431
9432 if (fg)
9433 color = HL_TABLE()[id - 1].sg_gui_fg;
9434 else
9435 color = HL_TABLE()[id - 1].sg_gui_bg;
9436
9437 if (color == INVALCOLOR)
9438 return 0L;
9439
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009440 return GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009441}
9442#endif
9443
9444/*
9445 * Output the syntax list header.
9446 * Return TRUE when started a new line.
9447 */
9448 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009449syn_list_header(
9450 int did_header, /* did header already */
9451 int outlen, /* length of string that comes */
9452 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009453{
9454 int endcol = 19;
9455 int newline = TRUE;
9456
9457 if (!did_header)
9458 {
9459 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009460 if (got_int)
9461 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009462 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9463 endcol = 15;
9464 }
9465 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009466 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009467 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009468 if (got_int)
9469 return TRUE;
9470 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009471 else
9472 {
9473 if (msg_col >= endcol) /* wrap around is like starting a new line */
9474 newline = FALSE;
9475 }
9476
9477 if (msg_col >= endcol) /* output at least one space */
9478 endcol = msg_col + 1;
9479 if (Columns <= endcol) /* avoid hang for tiny window */
9480 endcol = Columns - 1;
9481
9482 msg_advance(endcol);
9483
9484 /* Show "xxx" with the attributes. */
9485 if (!did_header)
9486 {
9487 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9488 msg_putchar(' ');
9489 }
9490
9491 return newline;
9492}
9493
9494/*
9495 * Set the attribute numbers for a highlight group.
9496 * Called after one of the attributes has changed.
9497 */
9498 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009499set_hl_attr(
9500 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009501{
9502 attrentry_T at_en;
9503 struct hl_group *sgp = HL_TABLE() + idx;
9504
9505 /* The "Normal" group doesn't need an attribute number */
9506 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9507 return;
9508
9509#ifdef FEAT_GUI
9510 /*
9511 * For the GUI mode: If there are other than "normal" highlighting
9512 * attributes, need to allocate an attr number.
9513 */
9514 if (sgp->sg_gui_fg == INVALCOLOR
9515 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009516 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009517 && sgp->sg_font == NOFONT
9518# ifdef FEAT_XFONTSET
9519 && sgp->sg_fontset == NOFONTSET
9520# endif
9521 )
9522 {
9523 sgp->sg_gui_attr = sgp->sg_gui;
9524 }
9525 else
9526 {
9527 at_en.ae_attr = sgp->sg_gui;
9528 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9529 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009530 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009531 at_en.ae_u.gui.font = sgp->sg_font;
9532# ifdef FEAT_XFONTSET
9533 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9534# endif
9535 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9536 }
9537#endif
9538 /*
9539 * For the term mode: If there are other than "normal" highlighting
9540 * attributes, need to allocate an attr number.
9541 */
9542 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9543 sgp->sg_term_attr = sgp->sg_term;
9544 else
9545 {
9546 at_en.ae_attr = sgp->sg_term;
9547 at_en.ae_u.term.start = sgp->sg_start;
9548 at_en.ae_u.term.stop = sgp->sg_stop;
9549 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9550 }
9551
9552 /*
9553 * For the color term mode: If there are other than "normal"
9554 * highlighting attributes, need to allocate an attr number.
9555 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009556 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009557# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009558 && sgp->sg_gui_fg == INVALCOLOR
9559 && sgp->sg_gui_bg == INVALCOLOR
9560# endif
9561 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00009562 sgp->sg_cterm_attr = sgp->sg_cterm;
9563 else
9564 {
9565 at_en.ae_attr = sgp->sg_cterm;
9566 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9567 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009568# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarcafafb32018-02-22 21:07:09 +01009569# ifdef WIN3264
9570 {
9571 int id;
9572 guicolor_T fg, bg;
9573
9574 id = syn_name2id((char_u *)"Normal");
9575 if (id > 0)
9576 {
9577 syn_id2colors(id, &fg, &bg);
9578 if (sgp->sg_gui_fg == INVALCOLOR)
9579 sgp->sg_gui_fg = fg;
9580 if (sgp->sg_gui_bg == INVALCOLOR)
9581 sgp->sg_gui_bg = bg;
9582 }
9583
9584 }
9585# endif
Bram Moolenaar187147a2016-05-01 13:09:57 +02009586 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
9587 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaard4fc5772018-02-27 14:39:03 +01009588 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
9589 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
9590 {
9591 /* If both fg and bg are invalid fall back to the cterm colors.
9592 * Helps when the GUI only uses an attribute, e.g. undercurl. */
9593 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
9594 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
9595 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009596# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009597 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9598 }
9599}
9600
9601/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009602 * Lookup a highlight group name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009603 * If it is not found, 0 is returned.
9604 */
9605 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009606syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009607{
9608 int i;
9609 char_u name_u[200];
9610
9611 /* Avoid using stricmp() too much, it's slow on some systems */
9612 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9613 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009614 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009615 vim_strup(name_u);
9616 for (i = highlight_ga.ga_len; --i >= 0; )
9617 if (HL_TABLE()[i].sg_name_u != NULL
9618 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9619 break;
9620 return i + 1;
9621}
9622
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02009623/*
9624 * Lookup a highlight group name and return its attributes.
9625 * Return zero if not found.
9626 */
9627 int
9628syn_name2attr(char_u *name)
9629{
9630 int id = syn_name2id(name);
9631
9632 if (id != 0)
Bram Moolenaar76301952017-09-22 13:53:37 +02009633 return syn_id2attr(id);
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02009634 return 0;
9635}
9636
Bram Moolenaar071d4272004-06-13 20:20:40 +00009637#if defined(FEAT_EVAL) || defined(PROTO)
9638/*
9639 * Return TRUE if highlight group "name" exists.
9640 */
9641 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009642highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009643{
9644 return (syn_name2id(name) > 0);
9645}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009646
9647# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9648/*
9649 * Return the name of highlight group "id".
9650 * When not a valid ID return an empty string.
9651 */
9652 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009653syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009654{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009655 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009656 return (char_u *)"";
9657 return HL_TABLE()[id - 1].sg_name;
9658}
9659# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009660#endif
9661
9662/*
9663 * Like syn_name2id(), but take a pointer + length argument.
9664 */
9665 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009666syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009667{
9668 char_u *name;
9669 int id = 0;
9670
9671 name = vim_strnsave(linep, len);
9672 if (name != NULL)
9673 {
9674 id = syn_name2id(name);
9675 vim_free(name);
9676 }
9677 return id;
9678}
9679
9680/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009681 * Find highlight group name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009682 * The argument is a pointer to the name and the length of the name.
9683 * If it doesn't exist yet, a new entry is created.
9684 * Return 0 for failure.
9685 */
9686 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009687syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009688{
9689 int id;
9690 char_u *name;
9691
9692 name = vim_strnsave(pp, len);
9693 if (name == NULL)
9694 return 0;
9695
9696 id = syn_name2id(name);
9697 if (id == 0) /* doesn't exist yet */
9698 id = syn_add_group(name);
9699 else
9700 vim_free(name);
9701 return id;
9702}
9703
9704/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009705 * Add new highlight group and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009706 * "name" must be an allocated string, it will be consumed.
9707 * Return 0 for failure.
9708 */
9709 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009710syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009711{
9712 char_u *p;
9713
9714 /* Check that the name is ASCII letters, digits and underscore. */
9715 for (p = name; *p != NUL; ++p)
9716 {
9717 if (!vim_isprintc(*p))
9718 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01009719 emsg(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009720 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009721 return 0;
9722 }
9723 else if (!ASCII_ISALNUM(*p) && *p != '_')
9724 {
9725 /* This is an error, but since there previously was no check only
9726 * give a warning. */
Bram Moolenaar8820b482017-03-16 17:23:31 +01009727 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009728 MSG(_("W18: Invalid character in group name"));
9729 break;
9730 }
9731 }
9732
9733 /*
9734 * First call for this growarray: init growing array.
9735 */
9736 if (highlight_ga.ga_data == NULL)
9737 {
9738 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9739 highlight_ga.ga_growsize = 10;
9740 }
9741
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009742 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009743 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01009744 emsg(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009745 vim_free(name);
9746 return 0;
9747 }
9748
Bram Moolenaar071d4272004-06-13 20:20:40 +00009749 /*
9750 * Make room for at least one other syntax_highlight entry.
9751 */
9752 if (ga_grow(&highlight_ga, 1) == FAIL)
9753 {
9754 vim_free(name);
9755 return 0;
9756 }
9757
9758 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9759 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9760 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009761#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009762 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9763 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009764# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009765 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009766# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009767#endif
9768 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009769
9770 return highlight_ga.ga_len; /* ID is index plus one */
9771}
9772
9773/*
9774 * When, just after calling syn_add_group(), an error is discovered, this
9775 * function deletes the new name.
9776 */
9777 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009778syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009779{
9780 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009781 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9782 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9783}
9784
9785/*
9786 * Translate a group ID to highlight attributes.
9787 */
9788 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009789syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009790{
9791 int attr;
9792 struct hl_group *sgp;
9793
9794 hl_id = syn_get_final_id(hl_id);
9795 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9796
9797#ifdef FEAT_GUI
9798 /*
9799 * Only use GUI attr when the GUI is being used.
9800 */
9801 if (gui.in_use)
9802 attr = sgp->sg_gui_attr;
9803 else
9804#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009805 if (IS_CTERM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009806 attr = sgp->sg_cterm_attr;
9807 else
9808 attr = sgp->sg_term_attr;
9809
9810 return attr;
9811}
9812
Bram Moolenaarc71053c2017-09-14 00:00:44 +02009813#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009814/*
9815 * Get the GUI colors and attributes for a group ID.
9816 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9817 */
9818 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009819syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009820{
9821 struct hl_group *sgp;
9822
9823 hl_id = syn_get_final_id(hl_id);
9824 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9825
9826 *fgp = sgp->sg_gui_fg;
9827 *bgp = sgp->sg_gui_bg;
9828 return sgp->sg_gui;
9829}
9830#endif
9831
Bram Moolenaara772baf2018-05-20 13:35:44 +02009832#if (defined(WIN3264) \
9833 && !defined(FEAT_GUI_W32) \
9834 && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
Bram Moolenaarc71053c2017-09-14 00:00:44 +02009835 void
9836syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
9837{
9838 struct hl_group *sgp;
9839
9840 hl_id = syn_get_final_id(hl_id);
9841 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9842 *fgp = sgp->sg_cterm_fg - 1;
9843 *bgp = sgp->sg_cterm_bg - 1;
9844}
9845#endif
9846
Bram Moolenaar071d4272004-06-13 20:20:40 +00009847/*
9848 * Translate a group ID to the final group ID (following links).
9849 */
9850 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009851syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009852{
9853 int count;
9854 struct hl_group *sgp;
9855
9856 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9857 return 0; /* Can be called from eval!! */
9858
9859 /*
9860 * Follow links until there is no more.
9861 * Look out for loops! Break after 100 links.
9862 */
9863 for (count = 100; --count >= 0; )
9864 {
9865 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9866 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9867 break;
9868 hl_id = sgp->sg_link;
9869 }
9870
9871 return hl_id;
9872}
9873
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01009874#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009875/*
9876 * Call this function just after the GUI has started.
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01009877 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009878 * It finds the font and color handles for the highlighting groups.
9879 */
9880 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009881highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009882{
9883 int idx;
9884
9885 /* First get the colors from the "Normal" and "Menu" group, if set */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009886 if (USE_24BIT)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009887 set_normal_colors();
Bram Moolenaar071d4272004-06-13 20:20:40 +00009888
9889 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9890 gui_do_one_color(idx, FALSE, FALSE);
9891
9892 highlight_changed();
9893}
9894
9895 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009896gui_do_one_color(
9897 int idx,
Bram Moolenaar380130f2016-04-22 11:24:43 +02009898 int do_menu UNUSED, /* TRUE: might set the menu font */
9899 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009900{
9901 int didit = FALSE;
9902
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009903# ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009904# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009905 if (gui.in_use)
9906# endif
9907 if (HL_TABLE()[idx].sg_font_name != NULL)
9908 {
9909 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009910 do_tooltip, TRUE);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009911 didit = TRUE;
9912 }
9913# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009914 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9915 {
9916 HL_TABLE()[idx].sg_gui_fg =
9917 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9918 didit = TRUE;
9919 }
9920 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9921 {
9922 HL_TABLE()[idx].sg_gui_bg =
9923 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9924 didit = TRUE;
9925 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009926# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009927 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9928 {
9929 HL_TABLE()[idx].sg_gui_sp =
9930 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9931 didit = TRUE;
9932 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009933# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009934 if (didit) /* need to get a new attr number */
9935 set_hl_attr(idx);
9936}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009937#endif
9938
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009939#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
9940/*
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02009941 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009942 */
9943 static void
9944combine_stl_hlt(
9945 int id,
9946 int id_S,
9947 int id_alt,
9948 int hlcnt,
9949 int i,
9950 int hlf,
9951 int *table)
9952{
9953 struct hl_group *hlt = HL_TABLE();
9954
9955 if (id_alt == 0)
9956 {
9957 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
9958 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
9959 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
9960# if defined(FEAT_GUI) || defined(FEAT_EVAL)
9961 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
9962# endif
9963 }
9964 else
9965 mch_memmove(&hlt[hlcnt + i],
9966 &hlt[id_alt - 1],
9967 sizeof(struct hl_group));
9968 hlt[hlcnt + i].sg_link = 0;
9969
9970 hlt[hlcnt + i].sg_term ^=
9971 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9972 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9973 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9974 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9975 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9976 hlt[hlcnt + i].sg_cterm ^=
9977 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9978 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9979 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9980 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9981 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
9982# if defined(FEAT_GUI) || defined(FEAT_EVAL)
9983 hlt[hlcnt + i].sg_gui ^=
9984 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
9985# endif
9986# ifdef FEAT_GUI
9987 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9988 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9989 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9990 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
9991 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9992 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
9993 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9994 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9995# ifdef FEAT_XFONTSET
9996 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9997 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9998# endif
9999# endif
10000 highlight_ga.ga_len = hlcnt + i + 1;
10001 set_hl_attr(hlcnt + i); /* At long last we can apply */
10002 table[i] = syn_id2attr(hlcnt + i + 1);
10003}
10004#endif
10005
Bram Moolenaar071d4272004-06-13 20:20:40 +000010006/*
10007 * Translate the 'highlight' option into attributes in highlight_attr[] and
10008 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
10009 * corresponding highlights to use on top of HLF_SNC is computed.
10010 * Called only when the 'highlight' option has been changed and upon first
10011 * screen redraw after any :highlight command.
10012 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
10013 */
10014 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010015highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010016{
10017 int hlf;
10018 int i;
10019 char_u *p;
10020 int attr;
10021 char_u *end;
10022 int id;
10023#ifdef USER_HIGHLIGHT
10024 char_u userhl[10];
10025# ifdef FEAT_STL_OPT
Bram Moolenaar071d4272004-06-13 20:20:40 +000010026 int id_S = -1;
Bram Moolenaar61859032018-03-20 13:00:25 +010010027 int id_SNC = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010028# ifdef FEAT_TERMINAL
Bram Moolenaar61859032018-03-20 13:00:25 +010010029 int id_ST = 0;
10030 int id_STNC = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010031# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010032 int hlcnt;
10033# endif
10034#endif
10035 static int hl_flags[HLF_COUNT] = HL_FLAGS;
10036
10037 need_highlight_changed = FALSE;
10038
10039 /*
10040 * Clear all attributes.
10041 */
10042 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
10043 highlight_attr[hlf] = 0;
10044
10045 /*
10046 * First set all attributes to their default value.
10047 * Then use the attributes from the 'highlight' option.
10048 */
10049 for (i = 0; i < 2; ++i)
10050 {
10051 if (i)
10052 p = p_hl;
10053 else
10054 p = get_highlight_default();
10055 if (p == NULL) /* just in case */
10056 continue;
10057
10058 while (*p)
10059 {
10060 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
10061 if (hl_flags[hlf] == *p)
10062 break;
10063 ++p;
10064 if (hlf == (int)HLF_COUNT || *p == NUL)
10065 return FAIL;
10066
10067 /*
10068 * Allow several hl_flags to be combined, like "bu" for
10069 * bold-underlined.
10070 */
10071 attr = 0;
10072 for ( ; *p && *p != ','; ++p) /* parse upto comma */
10073 {
Bram Moolenaar1c465442017-03-12 20:10:05 +010010074 if (VIM_ISWHITE(*p)) /* ignore white space */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010075 continue;
10076
10077 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
10078 return FAIL;
10079
10080 switch (*p)
10081 {
10082 case 'b': attr |= HL_BOLD;
10083 break;
10084 case 'i': attr |= HL_ITALIC;
10085 break;
10086 case '-':
10087 case 'n': /* no highlighting */
10088 break;
10089 case 'r': attr |= HL_INVERSE;
10090 break;
10091 case 's': attr |= HL_STANDOUT;
10092 break;
10093 case 'u': attr |= HL_UNDERLINE;
10094 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +000010095 case 'c': attr |= HL_UNDERCURL;
10096 break;
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +020010097 case 't': attr |= HL_STRIKETHROUGH;
10098 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010099 case ':': ++p; /* highlight group name */
10100 if (attr || *p == NUL) /* no combinations */
10101 return FAIL;
10102 end = vim_strchr(p, ',');
10103 if (end == NULL)
10104 end = p + STRLEN(p);
10105 id = syn_check_group(p, (int)(end - p));
10106 if (id == 0)
10107 return FAIL;
10108 attr = syn_id2attr(id);
10109 p = end - 1;
10110#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
10111 if (hlf == (int)HLF_SNC)
10112 id_SNC = syn_get_final_id(id);
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010113# ifdef FEAT_TERMINAL
10114 else if (hlf == (int)HLF_ST)
10115 id_ST = syn_get_final_id(id);
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010116 else if (hlf == (int)HLF_STNC)
10117 id_STNC = syn_get_final_id(id);
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010118# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010119 else if (hlf == (int)HLF_S)
10120 id_S = syn_get_final_id(id);
10121#endif
10122 break;
10123 default: return FAIL;
10124 }
10125 }
10126 highlight_attr[hlf] = attr;
10127
10128 p = skip_to_option_part(p); /* skip comma and spaces */
10129 }
10130 }
10131
10132#ifdef USER_HIGHLIGHT
10133 /* Setup the user highlights
10134 *
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010135 * Temporarily utilize 28 more hl entries:
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010136 * 9 for User1-User9 combined with StatusLineNC
10137 * 9 for User1-User9 combined with StatusLineTerm
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010138 * 9 for User1-User9 combined with StatusLineTermNC
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010139 * 1 for StatusLine default
10140 * Have to be in there simultaneously in case of table overflows in
10141 * get_attr_entry()
Bram Moolenaar071d4272004-06-13 20:20:40 +000010142 */
10143# ifdef FEAT_STL_OPT
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010144 if (ga_grow(&highlight_ga, 28) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010145 return FAIL;
10146 hlcnt = highlight_ga.ga_len;
Bram Moolenaard6a7b3e2017-08-19 21:35:35 +020010147 if (id_S == -1)
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010148 {
10149 /* Make sure id_S is always valid to simplify code below. Use the last
10150 * entry. */
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010151 vim_memset(&HL_TABLE()[hlcnt + 27], 0, sizeof(struct hl_group));
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010152 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
10153 id_S = hlcnt + 19;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010154 }
10155# endif
10156 for (i = 0; i < 9; i++)
10157 {
10158 sprintf((char *)userhl, "User%d", i + 1);
10159 id = syn_name2id(userhl);
10160 if (id == 0)
10161 {
10162 highlight_user[i] = 0;
10163# ifdef FEAT_STL_OPT
10164 highlight_stlnc[i] = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010165# ifdef FEAT_TERMINAL
10166 highlight_stlterm[i] = 0;
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010167 highlight_stltermnc[i] = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010168# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010169# endif
10170 }
10171 else
10172 {
Bram Moolenaar071d4272004-06-13 20:20:40 +000010173 highlight_user[i] = syn_id2attr(id);
10174# ifdef FEAT_STL_OPT
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010175 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
10176 HLF_SNC, highlight_stlnc);
10177# ifdef FEAT_TERMINAL
10178 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
10179 HLF_ST, highlight_stlterm);
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010180 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
10181 HLF_STNC, highlight_stltermnc);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010182# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010183# endif
10184 }
10185 }
10186# ifdef FEAT_STL_OPT
10187 highlight_ga.ga_len = hlcnt;
10188# endif
10189
10190#endif /* USER_HIGHLIGHT */
10191
10192 return OK;
10193}
10194
Bram Moolenaar4f688582007-07-24 12:34:30 +000010195#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010196
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010010197static void highlight_list(void);
10198static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010199
10200/*
10201 * Handle command line completion for :highlight command.
10202 */
10203 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010204set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010205{
10206 char_u *p;
10207
10208 /* Default: expand group names */
10209 xp->xp_context = EXPAND_HIGHLIGHT;
10210 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +000010211 include_link = 2;
10212 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010213
10214 /* (part of) subcommand already typed */
10215 if (*arg != NUL)
10216 {
10217 p = skiptowhite(arg);
10218 if (*p != NUL) /* past "default" or group name */
10219 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010220 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010221 if (STRNCMP("default", arg, p - arg) == 0)
10222 {
10223 arg = skipwhite(p);
10224 xp->xp_pattern = arg;
10225 p = skiptowhite(arg);
10226 }
10227 if (*p != NUL) /* past group name */
10228 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010229 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010230 if (arg[1] == 'i' && arg[0] == 'N')
10231 highlight_list();
10232 if (STRNCMP("link", arg, p - arg) == 0
10233 || STRNCMP("clear", arg, p - arg) == 0)
10234 {
10235 xp->xp_pattern = skipwhite(p);
10236 p = skiptowhite(xp->xp_pattern);
10237 if (*p != NUL) /* past first group name */
10238 {
10239 xp->xp_pattern = skipwhite(p);
10240 p = skiptowhite(xp->xp_pattern);
10241 }
10242 }
10243 if (*p != NUL) /* past group name(s) */
10244 xp->xp_context = EXPAND_NOTHING;
10245 }
10246 }
10247 }
10248}
10249
10250/*
10251 * List highlighting matches in a nice way.
10252 */
10253 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010254highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010255{
10256 int i;
10257
10258 for (i = 10; --i >= 0; )
Bram Moolenaar8820b482017-03-16 17:23:31 +010010259 highlight_list_two(i, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +000010260 for (i = 40; --i >= 0; )
10261 highlight_list_two(99, 0);
10262}
10263
10264 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010265highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010266{
Bram Moolenaar112f3182012-06-01 13:18:53 +020010267 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010268 msg_clr_eos();
10269 out_flush();
10270 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
10271}
10272
10273#endif /* FEAT_CMDL_COMPL */
10274
10275#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
10276 || defined(FEAT_SIGNS) || defined(PROTO)
10277/*
10278 * Function given to ExpandGeneric() to obtain the list of group names.
Bram Moolenaar071d4272004-06-13 20:20:40 +000010279 */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010280 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010281get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010282{
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010283 return get_highlight_name_ext(xp, idx, TRUE);
10284}
10285
10286/*
10287 * Obtain a highlight group name.
10288 * When "skip_cleared" is TRUE don't return a cleared entry.
10289 */
10290 char_u *
10291get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
10292{
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010293 if (idx < 0)
10294 return NULL;
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010295
10296 /* Items are never removed from the table, skip the ones that were
10297 * cleared. */
10298 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
10299 return (char_u *)"";
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010300
Bram Moolenaar071d4272004-06-13 20:20:40 +000010301#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000010302 if (idx == highlight_ga.ga_len && include_none != 0)
10303 return (char_u *)"none";
10304 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010305 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +000010306 if (idx == highlight_ga.ga_len + include_none + include_default
10307 && include_link != 0)
10308 return (char_u *)"link";
10309 if (idx == highlight_ga.ga_len + include_none + include_default + 1
10310 && include_link != 0)
10311 return (char_u *)"clear";
10312#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +010010313 if (idx >= highlight_ga.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010314 return NULL;
10315 return HL_TABLE()[idx].sg_name;
10316}
10317#endif
10318
Bram Moolenaar4f688582007-07-24 12:34:30 +000010319#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010320/*
10321 * Free all the highlight group fonts.
10322 * Used when quitting for systems which need it.
10323 */
10324 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010325free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010326{
10327 int idx;
10328
10329 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
10330 {
10331 gui_mch_free_font(HL_TABLE()[idx].sg_font);
10332 HL_TABLE()[idx].sg_font = NOFONT;
10333# ifdef FEAT_XFONTSET
10334 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
10335 HL_TABLE()[idx].sg_fontset = NOFONTSET;
10336# endif
10337 }
10338
10339 gui_mch_free_font(gui.norm_font);
10340# ifdef FEAT_XFONTSET
10341 gui_mch_free_fontset(gui.fontset);
10342# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +020010343# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +000010344 gui_mch_free_font(gui.bold_font);
10345 gui_mch_free_font(gui.ital_font);
10346 gui_mch_free_font(gui.boldital_font);
10347# endif
10348}
10349#endif
10350
10351/**************************************
10352 * End of Highlighting stuff *
10353 **************************************/