blob: 79b2796d2b0d248c7ecb5dc2e8605cf3e557b1cf [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
61 scid_T sg_scriptID; /* script in which the group was last set */
62#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 int get_attr_entry(garray_T *table, attrentry_T *aep);
95static void syn_unadd_group(void);
96static void set_hl_attr(int idx);
97static void highlight_list_one(int id);
98static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
99static int syn_add_group(char_u *name);
100static int syn_list_header(int did_header, int outlen, int id);
101static int hl_has_settings(int idx, int check_link);
102static void highlight_clear(int idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000103
Bram Moolenaar61be73b2016-04-29 22:59:22 +0200104#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100105static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
Bram Moolenaar8a633e32016-04-21 21:10:14 +0200106#endif
107#ifdef FEAT_GUI
108static 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 +0100109static GuiFont font_name2handle(char_u *name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000110# ifdef FEAT_XFONTSET
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100111static GuiFontset fontset_name2handle(char_u *name, int fixed_width);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000112# endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100113static 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 +0000114#endif
115
116/*
117 * An attribute number is the index in attr_table plus ATTR_OFF.
118 */
119#define ATTR_OFF (HL_ALL + 1)
120
121#if defined(FEAT_SYN_HL) || defined(PROTO)
122
123#define SYN_NAMELEN 50 /* maximum length of a syntax name */
124
125/* different types of offsets that are possible */
126#define SPO_MS_OFF 0 /* match start offset */
127#define SPO_ME_OFF 1 /* match end offset */
128#define SPO_HS_OFF 2 /* highl. start offset */
129#define SPO_HE_OFF 3 /* highl. end offset */
130#define SPO_RS_OFF 4 /* region start offset */
131#define SPO_RE_OFF 5 /* region end offset */
132#define SPO_LC_OFF 6 /* leading context offset */
133#define SPO_COUNT 7
134
135static char *(spo_name_tab[SPO_COUNT]) =
136 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
137
138/*
139 * The patterns that are being searched for are stored in a syn_pattern.
140 * A match item consists of one pattern.
141 * A start/end item consists of n start patterns and m end patterns.
142 * A start/skip/end item consists of n start patterns, one skip pattern and m
143 * end patterns.
144 * For the latter two, the patterns are always consecutive: start-skip-end.
145 *
146 * A character offset can be given for the matched text (_m_start and _m_end)
147 * and for the actually highlighted text (_h_start and _h_end).
148 */
149typedef struct syn_pattern
150{
151 char sp_type; /* see SPTYPE_ defines below */
152 char sp_syncing; /* this item used for syncing */
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 Moolenaar071d4272004-06-13 20:20:40 +0000157 struct sp_syn sp_syn; /* struct passed to in_id_list() */
158 short sp_syn_match_id; /* highlight group ID of pattern */
159 char_u *sp_pattern; /* regexp to match, pattern */
160 regprog_T *sp_prog; /* regexp to match, program */
Bram Moolenaarf7512552013-06-06 14:55:19 +0200161#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200162 syn_time_T sp_time;
163#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000164 int sp_ic; /* ignore-case flag for sp_prog */
165 short sp_off_flags; /* see below */
166 int sp_offsets[SPO_COUNT]; /* offsets */
167 short *sp_cont_list; /* cont. group IDs, if non-zero */
168 short *sp_next_list; /* next group IDs, if non-zero */
169 int sp_sync_idx; /* sync item index (syncing only) */
170 int sp_line_id; /* ID of last line where tried */
171 int sp_startcol; /* next match in sp_line_id line */
172} synpat_T;
173
174/* The sp_off_flags are computed like this:
175 * offset from the start of the matched text: (1 << SPO_XX_OFF)
176 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
177 * When both are present, only one is used.
178 */
179
180#define SPTYPE_MATCH 1 /* match keyword with this group ID */
181#define SPTYPE_START 2 /* match a regexp, start of item */
182#define SPTYPE_END 3 /* match a regexp, end of item */
183#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
184
Bram Moolenaar071d4272004-06-13 20:20:40 +0000185
186#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
187
188#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
189
190/*
191 * Flags for b_syn_sync_flags:
192 */
193#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
194#define SF_MATCH 0x02 /* sync by matching a pattern */
195
196#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
197
Bram Moolenaar071d4272004-06-13 20:20:40 +0000198#define MAXKEYWLEN 80 /* maximum length of a keyword */
199
200/*
201 * The attributes of the syntax item that has been recognized.
202 */
203static int current_attr = 0; /* attr of current syntax word */
204#ifdef FEAT_EVAL
205static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000206static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000207#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200208#ifdef FEAT_CONCEAL
209static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200210static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200211static int current_sub_char = 0;
212#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000213
Bram Moolenaar217ad922005-03-20 22:37:15 +0000214typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000215{
216 char_u *scl_name; /* syntax cluster name */
217 char_u *scl_name_u; /* uppercase of scl_name */
218 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000219} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000220
221/*
222 * Methods of combining two clusters
223 */
224#define CLUSTER_REPLACE 1 /* replace first list with second */
225#define CLUSTER_ADD 2 /* add second list to first */
226#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
227
Bram Moolenaar217ad922005-03-20 22:37:15 +0000228#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000229
230/*
231 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200232 * 0 - 19999 normal syntax groups
233 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
234 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
235 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
236 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000237 */
Bram Moolenaar2dfb3862011-04-02 15:12:50 +0200238#define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */
Bram Moolenaar42431a72011-04-01 14:44:59 +0200239#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */
240#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */
241#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */
242
Bram Moolenaar42431a72011-04-01 14:44:59 +0200243#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */
244#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000245
246/*
247 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
248 * expand_filename(). Most of the other syntax commands don't need it, so
249 * instead of passing it to them, we stow it here.
250 */
251static char_u **syn_cmdlinep;
252
253/*
254 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200255 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000256 * rules in each ":syn include"'d file.
257 */
258static int current_syn_inc_tag = 0;
259static int running_syn_inc_tag = 0;
260
261/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000262 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
263 * This avoids adding a pointer to the hashtable item.
264 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
265 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
266 * HI2KE() converts a hashitem pointer to a var pointer.
267 */
268static keyentry_T dumkey;
269#define KE2HIKEY(kp) ((kp)->keyword)
270#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
271#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
272
273/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000274 * To reduce the time spent in keepend(), remember at which level in the state
275 * stack the first item with "keepend" is present. When "-1", there is no
276 * "keepend" on the stack.
277 */
278static int keepend_level = -1;
279
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200280static char msg_no_items[] = N_("No Syntax items defined for this buffer");
281
Bram Moolenaar071d4272004-06-13 20:20:40 +0000282/*
283 * For the current state we need to remember more than just the idx.
284 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
285 * (The end positions have the column number of the next char)
286 */
287typedef struct state_item
288{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000289 int si_idx; /* index of syntax pattern or
290 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000291 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000292 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000293 int si_m_lnum; /* lnum of the match */
294 int si_m_startcol; /* starting column of the match */
295 lpos_T si_m_endpos; /* just after end posn of the match */
296 lpos_T si_h_startpos; /* start position of the highlighting */
297 lpos_T si_h_endpos; /* end position of the highlighting */
298 lpos_T si_eoe_pos; /* end position of end pattern */
299 int si_end_idx; /* group ID for end pattern or zero */
300 int si_ends; /* if match ends before si_m_endpos */
301 int si_attr; /* attributes in this state */
302 long si_flags; /* HL_HAS_EOL flag in this state, and
303 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200304#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200305 int si_seqnr; /* sequence number */
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200306 int si_cchar; /* substitution character for conceal */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200307#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000308 short *si_cont_list; /* list of contained groups */
309 short *si_next_list; /* nextgroup IDs after this item ends */
310 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
311 * pattern */
312} stateitem_T;
313
314#define KEYWORD_IDX -1 /* value of si_idx for keywords */
315#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
316 but contained groups */
317
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200318#ifdef FEAT_CONCEAL
Bram Moolenaare7154eb2015-03-21 21:46:13 +0100319static int next_seqnr = 1; /* value to use for si_seqnr */
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200320#endif
321
Bram Moolenaar071d4272004-06-13 20:20:40 +0000322/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000323 * Struct to reduce the number of arguments to get_syn_options(), it's used
324 * very often.
325 */
326typedef struct
327{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000328 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000329 int keyword; /* TRUE for ":syn keyword" */
330 int *sync_idx; /* syntax item for "grouphere" argument, NULL
331 if not allowed */
332 char has_cont_list; /* TRUE if "cont_list" can be used */
333 short *cont_list; /* group IDs for "contains" argument */
334 short *cont_in_list; /* group IDs for "containedin" argument */
335 short *next_list; /* group IDs for "nextgroup" argument */
336} syn_opt_arg_T;
337
338/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000339 * The next possible match in the current line for any pattern is remembered,
340 * to avoid having to try for a match in each column.
341 * If next_match_idx == -1, not tried (in this line) yet.
342 * If next_match_col == MAXCOL, no match found in this line.
343 * (All end positions have the column of the char after the end)
344 */
345static int next_match_col; /* column for start of next match */
346static lpos_T next_match_m_endpos; /* position for end of next match */
347static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
348static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
349static int next_match_idx; /* index of matched item */
350static long next_match_flags; /* flags for next match */
351static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
352static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
353static int next_match_end_idx; /* ID of group for end pattn or zero */
354static reg_extmatch_T *next_match_extmatch = NULL;
355
356/*
357 * A state stack is an array of integers or stateitem_T, stored in a
358 * garray_T. A state stack is invalid if it's itemsize entry is zero.
359 */
360#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
361#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
362
363/*
364 * The current state (within the line) of the recognition engine.
365 * When current_state.ga_itemsize is 0 the current state is invalid.
366 */
367static win_T *syn_win; /* current window for highlighting */
368static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200369static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200370#ifdef FEAT_RELTIME
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200371static proftime_T *syn_tm; /* timeout limit */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200372#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000373static linenr_T current_lnum = 0; /* lnum of current state */
374static colnr_T current_col = 0; /* column of current state */
375static int current_state_stored = 0; /* TRUE if stored current state
376 * after setting current_finished */
377static int current_finished = 0; /* current line has been finished */
378static garray_T current_state /* current stack of state_items */
379 = {0, 0, 0, 0, NULL};
380static short *current_next_list = NULL; /* when non-zero, nextgroup list */
381static int current_next_flags = 0; /* flags for current_next_list */
382static int current_line_id = 0; /* unique number for current line */
383
384#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
385
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100386static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100387static void save_chartab(char_u *chartab);
388static void restore_chartab(char_u *chartab);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100389static int syn_match_linecont(linenr_T lnum);
390static void syn_start_line(void);
391static void syn_update_ends(int startofline);
392static void syn_stack_alloc(void);
393static int syn_stack_cleanup(void);
394static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
395static synstate_T *syn_stack_find_entry(linenr_T lnum);
396static synstate_T *store_current_state(void);
397static void load_current_state(synstate_T *from);
398static void invalidate_current_state(void);
399static int syn_stack_equal(synstate_T *sp);
400static void validate_current_state(void);
401static int syn_finish_line(int syncing);
402static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
403static int did_match_already(int idx, garray_T *gap);
404static stateitem_T *push_next_match(stateitem_T *cur_si);
405static void check_state_ends(void);
406static void update_si_attr(int idx);
407static void check_keepend(void);
408static void update_si_end(stateitem_T *sip, int startcol, int force);
409static short *copy_id_list(short *list);
410static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
411static int push_current_state(int idx);
412static void pop_current_state(void);
Bram Moolenaarf7512552013-06-06 14:55:19 +0200413#ifdef FEAT_PROFILE
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100414static void syn_clear_time(syn_time_T *tt);
415static void syntime_clear(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200416#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100417static int _RTLENTRYF syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200418#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100419static int syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200420#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100421static void syntime_report(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200422static int syn_time_on = FALSE;
423# define IF_SYN_TIME(p) (p)
424#else
425# define IF_SYN_TIME(p) NULL
426typedef int syn_time_T;
427#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000428
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100429static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
430static 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);
431static void clear_syn_state(synstate_T *p);
432static void clear_current_state(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000433
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100434static void limit_pos(lpos_T *pos, lpos_T *limit);
435static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
436static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
437static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
438static char_u *syn_getcurline(void);
439static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
440static int check_keyword_id(char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp);
441static void syn_cmd_case(exarg_T *eap, int syncing);
442static void syn_cmd_spell(exarg_T *eap, int syncing);
443static void syntax_sync_clear(void);
444static void syn_remove_pattern(synblock_T *block, int idx);
445static void syn_clear_pattern(synblock_T *block, int i);
446static void syn_clear_cluster(synblock_T *block, int i);
447static void syn_cmd_clear(exarg_T *eap, int syncing);
448static void syn_cmd_conceal(exarg_T *eap, int syncing);
449static void syn_clear_one(int id, int syncing);
450static void syn_cmd_on(exarg_T *eap, int syncing);
451static void syn_cmd_enable(exarg_T *eap, int syncing);
452static void syn_cmd_reset(exarg_T *eap, int syncing);
453static void syn_cmd_manual(exarg_T *eap, int syncing);
454static void syn_cmd_off(exarg_T *eap, int syncing);
455static void syn_cmd_onoff(exarg_T *eap, char *name);
456static void syn_cmd_list(exarg_T *eap, int syncing);
457static void syn_lines_msg(void);
458static void syn_match_msg(void);
459static void syn_stack_free_block(synblock_T *block);
460static void syn_list_one(int id, int syncing, int link_only);
461static void syn_list_cluster(int id);
462static void put_id_list(char_u *name, short *list, int attr);
463static void put_pattern(char *s, int c, synpat_T *spp, int attr);
464static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
465static void syn_clear_keyword(int id, hashtab_T *ht);
466static void clear_keywtab(hashtab_T *ht);
467static void add_keyword(char_u *name, int id, int flags, short *cont_in_list, short *next_list, int conceal_char);
468static char_u *get_group_name(char_u *arg, char_u **name_end);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100469static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_char, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100470static void syn_cmd_include(exarg_T *eap, int syncing);
471static void syn_cmd_iskeyword(exarg_T *eap, int syncing);
472static void syn_cmd_keyword(exarg_T *eap, int syncing);
473static void syn_cmd_match(exarg_T *eap, int syncing);
474static void syn_cmd_region(exarg_T *eap, int syncing);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000475#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100476static int _RTLENTRYF syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000477#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100478static int syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000479#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100480static void syn_cmd_cluster(exarg_T *eap, int syncing);
481static int syn_scl_name2id(char_u *name);
482static int syn_scl_namen2id(char_u *linep, int len);
483static int syn_check_cluster(char_u *pp, int len);
484static int syn_add_cluster(char_u *name);
485static void init_syn_patterns(void);
486static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
487static void syn_cmd_sync(exarg_T *eap, int syncing);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100488static int get_id_list(char_u **arg, int keylen, short **list, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100489static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
490static void syn_incl_toplevel(int id, int *flagsp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000491
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200492#if defined(FEAT_RELTIME) || defined(PROTO)
493/*
494 * Set the timeout used for syntax highlighting.
495 * Use NULL to reset, no timeout.
496 */
497 void
498syn_set_timeout(proftime_T *tm)
499{
500 syn_tm = tm;
501}
502#endif
503
Bram Moolenaar071d4272004-06-13 20:20:40 +0000504/*
505 * Start the syntax recognition for a line. This function is normally called
506 * from the screen updating, once for each displayed line.
507 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
508 * it. Careful: curbuf and curwin are likely to point to another buffer and
509 * window.
510 */
511 void
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200512syntax_start(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000513{
514 synstate_T *p;
515 synstate_T *last_valid = NULL;
516 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000517 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000518 linenr_T parsed_lnum;
519 linenr_T first_stored;
520 int dist;
Bram Moolenaar79518e22017-02-17 16:31:35 +0100521 static varnumber_T changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000522
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200523#ifdef FEAT_CONCEAL
524 current_sub_char = NUL;
525#endif
526
Bram Moolenaar071d4272004-06-13 20:20:40 +0000527 /*
528 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000529 * Also do this when a change was made, the current state may be invalid
530 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000531 */
Bram Moolenaarb681be12016-03-31 23:02:16 +0200532 if (syn_block != wp->w_s
533 || syn_buf != wp->w_buffer
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100534 || changedtick != CHANGEDTICK(syn_buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000535 {
536 invalidate_current_state();
537 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200538 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000539 }
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100540 changedtick = CHANGEDTICK(syn_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000541 syn_win = wp;
542
543 /*
544 * Allocate syntax stack when needed.
545 */
546 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200547 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000548 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200549 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000550
551 /*
552 * If the state of the end of the previous line is useful, store it.
553 */
554 if (VALID_STATE(&current_state)
555 && current_lnum < lnum
556 && current_lnum < syn_buf->b_ml.ml_line_count)
557 {
558 (void)syn_finish_line(FALSE);
559 if (!current_state_stored)
560 {
561 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000562 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000563 }
564
565 /*
566 * If the current_lnum is now the same as "lnum", keep the current
567 * state (this happens very often!). Otherwise invalidate
568 * current_state and figure it out below.
569 */
570 if (current_lnum != lnum)
571 invalidate_current_state();
572 }
573 else
574 invalidate_current_state();
575
576 /*
577 * Try to synchronize from a saved state in b_sst_array[].
578 * Only do this if lnum is not before and not to far beyond a saved state.
579 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200580 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000581 {
582 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200583 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000584 {
585 if (p->sst_lnum > lnum)
586 break;
587 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
588 {
589 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200590 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000591 last_min_valid = p;
592 }
593 }
594 if (last_min_valid != NULL)
595 load_current_state(last_min_valid);
596 }
597
598 /*
599 * If "lnum" is before or far beyond a line with a saved state, need to
600 * re-synchronize.
601 */
602 if (INVALID_STATE(&current_state))
603 {
604 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200605 if (current_lnum == 1)
606 /* First line is always valid, no matter "minlines". */
607 first_stored = 1;
608 else
609 /* Need to parse "minlines" lines before state can be considered
610 * valid to store. */
611 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000612 }
613 else
614 first_stored = current_lnum;
615
616 /*
617 * Advance from the sync point or saved state until the current line.
618 * Save some entries for syncing with later on.
619 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200620 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000621 dist = 999999;
622 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200623 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000624 while (current_lnum < lnum)
625 {
626 syn_start_line();
627 (void)syn_finish_line(FALSE);
628 ++current_lnum;
629
630 /* If we parsed at least "minlines" lines or started at a valid
631 * state, the current state is considered valid. */
632 if (current_lnum >= first_stored)
633 {
634 /* Check if the saved state entry is for the current line and is
635 * equal to the current state. If so, then validate all saved
636 * states that depended on a change before the parsed line. */
637 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000638 prev = syn_stack_find_entry(current_lnum - 1);
639 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200640 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000641 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000642 sp = prev;
643 while (sp != NULL && sp->sst_lnum < current_lnum)
644 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000645 if (sp != NULL
646 && sp->sst_lnum == current_lnum
647 && syn_stack_equal(sp))
648 {
649 parsed_lnum = current_lnum;
650 prev = sp;
651 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
652 {
653 if (sp->sst_lnum <= lnum)
654 /* valid state before desired line, use this one */
655 prev = sp;
656 else if (sp->sst_change_lnum == 0)
657 /* past saved states depending on change, break here. */
658 break;
659 sp->sst_change_lnum = 0;
660 sp = sp->sst_next;
661 }
662 load_current_state(prev);
663 }
664 /* Store the state at this line when it's the first one, the line
665 * where we start parsing, or some distance from the previously
666 * saved state. But only when parsed at least 'minlines'. */
667 else if (prev == NULL
668 || current_lnum == lnum
669 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000670 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000671 }
672
673 /* This can take a long time: break when CTRL-C pressed. The current
674 * state will be wrong then. */
675 line_breakcheck();
676 if (got_int)
677 {
678 current_lnum = lnum;
679 break;
680 }
681 }
682
683 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000684}
685
686/*
687 * We cannot simply discard growarrays full of state_items or buf_states; we
688 * have to manually release their extmatch pointers first.
689 */
690 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100691clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000692{
693 int i;
694 garray_T *gap;
695
696 if (p->sst_stacksize > SST_FIX_STATES)
697 {
698 gap = &(p->sst_union.sst_ga);
699 for (i = 0; i < gap->ga_len; i++)
700 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
701 ga_clear(gap);
702 }
703 else
704 {
705 for (i = 0; i < p->sst_stacksize; i++)
706 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
707 }
708}
709
710/*
711 * Cleanup the current_state stack.
712 */
713 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100714clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000715{
716 int i;
717 stateitem_T *sip;
718
719 sip = (stateitem_T *)(current_state.ga_data);
720 for (i = 0; i < current_state.ga_len; i++)
721 unref_extmatch(sip[i].si_extmatch);
722 ga_clear(&current_state);
723}
724
725/*
726 * Try to find a synchronisation point for line "lnum".
727 *
728 * This sets current_lnum and the current state. One of three methods is
729 * used:
730 * 1. Search backwards for the end of a C-comment.
731 * 2. Search backwards for given sync patterns.
732 * 3. Simply start on a given number of lines above "lnum".
733 */
734 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100735syn_sync(
736 win_T *wp,
737 linenr_T start_lnum,
738 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000739{
740 buf_T *curbuf_save;
741 win_T *curwin_save;
742 pos_T cursor_save;
743 int idx;
744 linenr_T lnum;
745 linenr_T end_lnum;
746 linenr_T break_lnum;
747 int had_sync_point;
748 stateitem_T *cur_si;
749 synpat_T *spp;
750 char_u *line;
751 int found_flags = 0;
752 int found_match_idx = 0;
753 linenr_T found_current_lnum = 0;
754 int found_current_col= 0;
755 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000756 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000757
758 /*
759 * Clear any current state that might be hanging around.
760 */
761 invalidate_current_state();
762
763 /*
764 * Start at least "minlines" back. Default starting point for parsing is
765 * there.
766 * Start further back, to avoid that scrolling backwards will result in
767 * resyncing for every line. Now it resyncs only one out of N lines,
768 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
769 * Watch out for overflow when minlines is MAXLNUM.
770 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200771 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000772 start_lnum = 1;
773 else
774 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200775 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000776 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200777 else if (syn_block->b_syn_sync_minlines < 10)
778 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000779 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200780 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
781 if (syn_block->b_syn_sync_maxlines != 0
782 && lnum > syn_block->b_syn_sync_maxlines)
783 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000784 if (lnum >= start_lnum)
785 start_lnum = 1;
786 else
787 start_lnum -= lnum;
788 }
789 current_lnum = start_lnum;
790
791 /*
792 * 1. Search backwards for the end of a C-style comment.
793 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200794 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000795 {
796 /* Need to make syn_buf the current buffer for a moment, to be able to
797 * use find_start_comment(). */
798 curwin_save = curwin;
799 curwin = wp;
800 curbuf_save = curbuf;
801 curbuf = syn_buf;
802
803 /*
804 * Skip lines that end in a backslash.
805 */
806 for ( ; start_lnum > 1; --start_lnum)
807 {
808 line = ml_get(start_lnum - 1);
809 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
810 break;
811 }
812 current_lnum = start_lnum;
813
814 /* set cursor to start of search */
815 cursor_save = wp->w_cursor;
816 wp->w_cursor.lnum = start_lnum;
817 wp->w_cursor.col = 0;
818
819 /*
820 * If the line is inside a comment, need to find the syntax item that
821 * defines the comment.
822 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
823 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200824 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000825 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200826 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
827 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
828 == syn_block->b_syn_sync_id
829 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000830 {
831 validate_current_state();
832 if (push_current_state(idx) == OK)
833 update_si_attr(current_state.ga_len - 1);
834 break;
835 }
836 }
837
838 /* restore cursor and buffer */
839 wp->w_cursor = cursor_save;
840 curwin = curwin_save;
841 curbuf = curbuf_save;
842 }
843
844 /*
845 * 2. Search backwards for given sync patterns.
846 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200847 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000848 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200849 if (syn_block->b_syn_sync_maxlines != 0
850 && start_lnum > syn_block->b_syn_sync_maxlines)
851 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000852 else
853 break_lnum = 0;
854
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000855 found_m_endpos.lnum = 0;
856 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000857 end_lnum = start_lnum;
858 lnum = start_lnum;
859 while (--lnum > break_lnum)
860 {
861 /* This can take a long time: break when CTRL-C pressed. */
862 line_breakcheck();
863 if (got_int)
864 {
865 invalidate_current_state();
866 current_lnum = start_lnum;
867 break;
868 }
869
870 /* Check if we have run into a valid saved state stack now. */
871 if (last_valid != NULL && lnum == last_valid->sst_lnum)
872 {
873 load_current_state(last_valid);
874 break;
875 }
876
877 /*
878 * Check if the previous line has the line-continuation pattern.
879 */
880 if (lnum > 1 && syn_match_linecont(lnum - 1))
881 continue;
882
883 /*
884 * Start with nothing on the state stack
885 */
886 validate_current_state();
887
888 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
889 {
890 syn_start_line();
891 for (;;)
892 {
893 had_sync_point = syn_finish_line(TRUE);
894 /*
895 * When a sync point has been found, remember where, and
896 * continue to look for another one, further on in the line.
897 */
898 if (had_sync_point && current_state.ga_len)
899 {
900 cur_si = &CUR_STATE(current_state.ga_len - 1);
901 if (cur_si->si_m_endpos.lnum > start_lnum)
902 {
903 /* ignore match that goes to after where started */
904 current_lnum = end_lnum;
905 break;
906 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000907 if (cur_si->si_idx < 0)
908 {
909 /* Cannot happen? */
910 found_flags = 0;
911 found_match_idx = KEYWORD_IDX;
912 }
913 else
914 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200915 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000916 found_flags = spp->sp_flags;
917 found_match_idx = spp->sp_sync_idx;
918 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000919 found_current_lnum = current_lnum;
920 found_current_col = current_col;
921 found_m_endpos = cur_si->si_m_endpos;
922 /*
923 * Continue after the match (be aware of a zero-length
924 * match).
925 */
926 if (found_m_endpos.lnum > current_lnum)
927 {
928 current_lnum = found_m_endpos.lnum;
929 current_col = found_m_endpos.col;
930 if (current_lnum >= end_lnum)
931 break;
932 }
933 else if (found_m_endpos.col > current_col)
934 current_col = found_m_endpos.col;
935 else
936 ++current_col;
937
938 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000939 * an item that ends here, need to do that now. Be
940 * careful not to go past the NUL. */
941 prev_current_col = current_col;
942 if (syn_getcurline()[current_col] != NUL)
943 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000944 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000945 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000946 }
947 else
948 break;
949 }
950 }
951
952 /*
953 * If a sync point was encountered, break here.
954 */
955 if (found_flags)
956 {
957 /*
958 * Put the item that was specified by the sync point on the
959 * state stack. If there was no item specified, make the
960 * state stack empty.
961 */
962 clear_current_state();
963 if (found_match_idx >= 0
964 && push_current_state(found_match_idx) == OK)
965 update_si_attr(current_state.ga_len - 1);
966
967 /*
968 * When using "grouphere", continue from the sync point
969 * match, until the end of the line. Parsing starts at
970 * the next line.
971 * For "groupthere" the parsing starts at start_lnum.
972 */
973 if (found_flags & HL_SYNC_HERE)
974 {
975 if (current_state.ga_len)
976 {
977 cur_si = &CUR_STATE(current_state.ga_len - 1);
978 cur_si->si_h_startpos.lnum = found_current_lnum;
979 cur_si->si_h_startpos.col = found_current_col;
980 update_si_end(cur_si, (int)current_col, TRUE);
981 check_keepend();
982 }
983 current_col = found_m_endpos.col;
984 current_lnum = found_m_endpos.lnum;
985 (void)syn_finish_line(FALSE);
986 ++current_lnum;
987 }
988 else
989 current_lnum = start_lnum;
990
991 break;
992 }
993
994 end_lnum = lnum;
995 invalidate_current_state();
996 }
997
998 /* Ran into start of the file or exceeded maximum number of lines */
999 if (lnum <= break_lnum)
1000 {
1001 invalidate_current_state();
1002 current_lnum = break_lnum + 1;
1003 }
1004 }
1005
1006 validate_current_state();
1007}
1008
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001009 static void
1010save_chartab(char_u *chartab)
1011{
1012 if (syn_block->b_syn_isk != empty_option)
1013 {
1014 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
1015 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
1016 (size_t)32);
1017 }
1018}
1019
1020 static void
1021restore_chartab(char_u *chartab)
1022{
1023 if (syn_win->w_s->b_syn_isk != empty_option)
1024 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
1025}
1026
Bram Moolenaar071d4272004-06-13 20:20:40 +00001027/*
1028 * Return TRUE if the line-continuation pattern matches in line "lnum".
1029 */
1030 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001031syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001032{
1033 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001034 int r;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001035 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001036
Bram Moolenaar860cae12010-06-05 23:22:07 +02001037 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001038 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001039 /* use syntax iskeyword option */
1040 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001041 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
1042 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001043 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001044 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001045 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001046 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001047 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001048 }
1049 return FALSE;
1050}
1051
1052/*
1053 * Prepare the current state for the start of a line.
1054 */
1055 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001056syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001057{
1058 current_finished = FALSE;
1059 current_col = 0;
1060
1061 /*
1062 * Need to update the end of a start/skip/end that continues from the
1063 * previous line and regions that have "keepend".
1064 */
1065 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001066 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001067 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001068 check_state_ends();
1069 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001070
1071 next_match_idx = -1;
1072 ++current_line_id;
Bram Moolenaarea20de82017-06-24 22:52:24 +02001073#ifdef FEAT_CONCEAL
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001074 next_seqnr = 1;
Bram Moolenaarea20de82017-06-24 22:52:24 +02001075#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001076}
1077
1078/*
1079 * Check for items in the stack that need their end updated.
1080 * When "startofline" is TRUE the last item is always updated.
1081 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1082 */
1083 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001084syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001085{
1086 stateitem_T *cur_si;
1087 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001088 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001089
1090 if (startofline)
1091 {
1092 /* Check for a match carried over from a previous line with a
1093 * contained region. The match ends as soon as the region ends. */
1094 for (i = 0; i < current_state.ga_len; ++i)
1095 {
1096 cur_si = &CUR_STATE(i);
1097 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001098 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001099 == SPTYPE_MATCH
1100 && cur_si->si_m_endpos.lnum < current_lnum)
1101 {
1102 cur_si->si_flags |= HL_MATCHCONT;
1103 cur_si->si_m_endpos.lnum = 0;
1104 cur_si->si_m_endpos.col = 0;
1105 cur_si->si_h_endpos = cur_si->si_m_endpos;
1106 cur_si->si_ends = TRUE;
1107 }
1108 }
1109 }
1110
1111 /*
1112 * Need to update the end of a start/skip/end that continues from the
1113 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001114 * influence contained items. If we've just removed "extend"
1115 * (startofline == 0) then we should update ends of normal regions
1116 * contained inside "keepend" because "extend" could have extended
1117 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001118 * Then check for items ending in column 0.
1119 */
1120 i = current_state.ga_len - 1;
1121 if (keepend_level >= 0)
1122 for ( ; i > keepend_level; --i)
1123 if (CUR_STATE(i).si_flags & HL_EXTEND)
1124 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001125
1126 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001127 for ( ; i < current_state.ga_len; ++i)
1128 {
1129 cur_si = &CUR_STATE(i);
1130 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001131 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001132 || (i == current_state.ga_len - 1 && startofline))
1133 {
1134 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1135 cur_si->si_h_startpos.lnum = current_lnum;
1136
1137 if (!(cur_si->si_flags & HL_MATCHCONT))
1138 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001139
1140 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1141 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001142 }
1143 }
1144 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001145}
1146
1147/****************************************
1148 * Handling of the state stack cache.
1149 */
1150
1151/*
1152 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1153 *
1154 * To speed up syntax highlighting, the state stack for the start of some
1155 * lines is cached. These entries can be used to start parsing at that point.
1156 *
1157 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1158 * valid entries. b_sst_first points to the first one, then follow sst_next.
1159 * The entries are sorted on line number. The first entry is often for line 2
1160 * (line 1 always starts with an empty stack).
1161 * There is also a list for free entries. This construction is used to avoid
1162 * having to allocate and free memory blocks too often.
1163 *
1164 * When making changes to the buffer, this is logged in b_mod_*. When calling
1165 * update_screen() to update the display, it will call
1166 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1167 * entries. The entries which are inside the changed area are removed,
1168 * because they must be recomputed. Entries below the changed have their line
1169 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1170 * set to indicate that a check must be made if the changed lines would change
1171 * the cached entry.
1172 *
1173 * When later displaying lines, an entry is stored for each line. Displayed
1174 * lines are likely to be displayed again, in which case the state at the
1175 * start of the line is needed.
1176 * For not displayed lines, an entry is stored for every so many lines. These
1177 * entries will be used e.g., when scrolling backwards. The distance between
1178 * entries depends on the number of lines in the buffer. For small buffers
1179 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1180 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1181 */
1182
Bram Moolenaar860cae12010-06-05 23:22:07 +02001183 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001184syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001185{
1186 synstate_T *p;
1187
1188 if (block->b_sst_array != NULL)
1189 {
1190 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1191 clear_syn_state(p);
Bram Moolenaard23a8232018-02-10 18:45:26 +01001192 VIM_CLEAR(block->b_sst_array);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001193 block->b_sst_len = 0;
1194 }
1195}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001196/*
1197 * Free b_sst_array[] for buffer "buf".
1198 * Used when syntax items changed to force resyncing everywhere.
1199 */
1200 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001201syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001202{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001203#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001204 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001205#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001206
Bram Moolenaar860cae12010-06-05 23:22:07 +02001207 syn_stack_free_block(block);
1208
Bram Moolenaar071d4272004-06-13 20:20:40 +00001209#ifdef FEAT_FOLDING
1210 /* When using "syntax" fold method, must update all folds. */
1211 FOR_ALL_WINDOWS(wp)
1212 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001213 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001214 foldUpdateAll(wp);
1215 }
1216#endif
1217}
1218
1219/*
1220 * Allocate the syntax state stack for syn_buf when needed.
1221 * If the number of entries in b_sst_array[] is much too big or a bit too
1222 * small, reallocate it.
1223 * Also used to allocate b_sst_array[] for the first time.
1224 */
1225 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001226syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001227{
1228 long len;
1229 synstate_T *to, *from;
1230 synstate_T *sstp;
1231
1232 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1233 if (len < SST_MIN_ENTRIES)
1234 len = SST_MIN_ENTRIES;
1235 else if (len > SST_MAX_ENTRIES)
1236 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001237 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001238 {
1239 /* Allocate 50% too much, to avoid reallocating too often. */
1240 len = syn_buf->b_ml.ml_line_count;
1241 len = (len + len / 2) / SST_DIST + Rows * 2;
1242 if (len < SST_MIN_ENTRIES)
1243 len = SST_MIN_ENTRIES;
1244 else if (len > SST_MAX_ENTRIES)
1245 len = SST_MAX_ENTRIES;
1246
Bram Moolenaar860cae12010-06-05 23:22:07 +02001247 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001248 {
1249 /* When shrinking the array, cleanup the existing stack.
1250 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001251 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001252 && syn_stack_cleanup())
1253 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001254 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1255 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001256 }
1257
1258 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1259 if (sstp == NULL) /* out of memory! */
1260 return;
1261
1262 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001263 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001264 {
1265 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001266 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001267 from = from->sst_next)
1268 {
1269 ++to;
1270 *to = *from;
1271 to->sst_next = to + 1;
1272 }
1273 }
1274 if (to != sstp - 1)
1275 {
1276 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001277 syn_block->b_sst_first = sstp;
1278 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001279 }
1280 else
1281 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001282 syn_block->b_sst_first = NULL;
1283 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001284 }
1285
1286 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001287 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001288 while (++to < sstp + len)
1289 to->sst_next = to + 1;
1290 (sstp + len - 1)->sst_next = NULL;
1291
Bram Moolenaar860cae12010-06-05 23:22:07 +02001292 vim_free(syn_block->b_sst_array);
1293 syn_block->b_sst_array = sstp;
1294 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001295 }
1296}
1297
1298/*
1299 * Check for changes in a buffer to affect stored syntax states. Uses the
1300 * b_mod_* fields.
1301 * Called from update_screen(), before screen is being updated, once for each
1302 * displayed buffer.
1303 */
1304 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001305syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001306{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001307 win_T *wp;
1308
1309 syn_stack_apply_changes_block(&buf->b_s, buf);
1310
1311 FOR_ALL_WINDOWS(wp)
1312 {
1313 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1314 syn_stack_apply_changes_block(wp->w_s, buf);
1315 }
1316}
1317
1318 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001319syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001320{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001321 synstate_T *p, *prev, *np;
1322 linenr_T n;
1323
Bram Moolenaar860cae12010-06-05 23:22:07 +02001324 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001325 return;
1326
1327 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001328 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001329 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001330 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001331 {
1332 n = p->sst_lnum + buf->b_mod_xlines;
1333 if (n <= buf->b_mod_bot)
1334 {
1335 /* this state is inside the changed area, remove it */
1336 np = p->sst_next;
1337 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001338 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001339 else
1340 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001341 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001342 p = np;
1343 continue;
1344 }
1345 /* This state is below the changed area. Remember the line
1346 * that needs to be parsed before this entry can be made valid
1347 * again. */
1348 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1349 {
1350 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1351 p->sst_change_lnum += buf->b_mod_xlines;
1352 else
1353 p->sst_change_lnum = buf->b_mod_top;
1354 }
1355 if (p->sst_change_lnum == 0
1356 || p->sst_change_lnum < buf->b_mod_bot)
1357 p->sst_change_lnum = buf->b_mod_bot;
1358
1359 p->sst_lnum = n;
1360 }
1361 prev = p;
1362 p = p->sst_next;
1363 }
1364}
1365
1366/*
1367 * Reduce the number of entries in the state stack for syn_buf.
1368 * Returns TRUE if at least one entry was freed.
1369 */
1370 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001371syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001372{
1373 synstate_T *p, *prev;
1374 disptick_T tick;
1375 int above;
1376 int dist;
1377 int retval = FALSE;
1378
Bram Moolenaar860cae12010-06-05 23:22:07 +02001379 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001380 return retval;
1381
1382 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001383 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001384 dist = 999999;
1385 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001386 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001387
1388 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001389 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001390 * be removed. Set "above" when the "tick" for the oldest entry is above
1391 * "b_sst_lasttick" (the display tick wraps around).
1392 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001393 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001394 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001395 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001396 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1397 {
1398 if (prev->sst_lnum + dist > p->sst_lnum)
1399 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001400 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001401 {
1402 if (!above || p->sst_tick < tick)
1403 tick = p->sst_tick;
1404 above = TRUE;
1405 }
1406 else if (!above && p->sst_tick < tick)
1407 tick = p->sst_tick;
1408 }
1409 }
1410
1411 /*
1412 * Go through the list to make the entries for the oldest tick at an
1413 * interval of several lines.
1414 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001415 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001416 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1417 {
1418 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1419 {
1420 /* Move this entry from used list to free list */
1421 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001422 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001423 p = prev;
1424 retval = TRUE;
1425 }
1426 }
1427 return retval;
1428}
1429
1430/*
1431 * Free the allocated memory for a syn_state item.
1432 * Move the entry into the free list.
1433 */
1434 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001435syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001436{
1437 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001438 p->sst_next = block->b_sst_firstfree;
1439 block->b_sst_firstfree = p;
1440 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001441}
1442
1443/*
1444 * Find an entry in the list of state stacks at or before "lnum".
1445 * Returns NULL when there is no entry or the first entry is after "lnum".
1446 */
1447 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001448syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001449{
1450 synstate_T *p, *prev;
1451
1452 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001453 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001454 {
1455 if (p->sst_lnum == lnum)
1456 return p;
1457 if (p->sst_lnum > lnum)
1458 break;
1459 }
1460 return prev;
1461}
1462
1463/*
1464 * Try saving the current state in b_sst_array[].
1465 * The current state must be valid for the start of the current_lnum line!
1466 */
1467 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001468store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001469{
1470 int i;
1471 synstate_T *p;
1472 bufstate_T *bp;
1473 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001474 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001475
1476 /*
1477 * If the current state contains a start or end pattern that continues
1478 * from the previous line, we can't use it. Don't store it then.
1479 */
1480 for (i = current_state.ga_len - 1; i >= 0; --i)
1481 {
1482 cur_si = &CUR_STATE(i);
1483 if (cur_si->si_h_startpos.lnum >= current_lnum
1484 || cur_si->si_m_endpos.lnum >= current_lnum
1485 || cur_si->si_h_endpos.lnum >= current_lnum
1486 || (cur_si->si_end_idx
1487 && cur_si->si_eoe_pos.lnum >= current_lnum))
1488 break;
1489 }
1490 if (i >= 0)
1491 {
1492 if (sp != NULL)
1493 {
1494 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001495 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001496 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001497 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001498 else
1499 {
1500 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001501 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001502 if (p->sst_next == sp)
1503 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001504 if (p != NULL) /* just in case */
1505 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001506 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001507 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508 sp = NULL;
1509 }
1510 }
1511 else if (sp == NULL || sp->sst_lnum != current_lnum)
1512 {
1513 /*
1514 * Add a new entry
1515 */
1516 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001517 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001518 {
1519 (void)syn_stack_cleanup();
1520 /* "sp" may have been moved to the freelist now */
1521 sp = syn_stack_find_entry(current_lnum);
1522 }
1523 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001524 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001525 sp = NULL;
1526 else
1527 {
1528 /* Take the first item from the free list and put it in the used
1529 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001530 p = syn_block->b_sst_firstfree;
1531 syn_block->b_sst_firstfree = p->sst_next;
1532 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001533 if (sp == NULL)
1534 {
1535 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001536 p->sst_next = syn_block->b_sst_first;
1537 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001538 }
1539 else
1540 {
1541 /* insert in list after *sp */
1542 p->sst_next = sp->sst_next;
1543 sp->sst_next = p;
1544 }
1545 sp = p;
1546 sp->sst_stacksize = 0;
1547 sp->sst_lnum = current_lnum;
1548 }
1549 }
1550 if (sp != NULL)
1551 {
1552 /* When overwriting an existing state stack, clear it first */
1553 clear_syn_state(sp);
1554 sp->sst_stacksize = current_state.ga_len;
1555 if (current_state.ga_len > SST_FIX_STATES)
1556 {
1557 /* Need to clear it, might be something remaining from when the
1558 * length was less than SST_FIX_STATES. */
1559 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1560 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1561 sp->sst_stacksize = 0;
1562 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001563 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001564 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1565 }
1566 else
1567 bp = sp->sst_union.sst_stack;
1568 for (i = 0; i < sp->sst_stacksize; ++i)
1569 {
1570 bp[i].bs_idx = CUR_STATE(i).si_idx;
1571 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001572#ifdef FEAT_CONCEAL
1573 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1574 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1575#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001576 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1577 }
1578 sp->sst_next_flags = current_next_flags;
1579 sp->sst_next_list = current_next_list;
1580 sp->sst_tick = display_tick;
1581 sp->sst_change_lnum = 0;
1582 }
1583 current_state_stored = TRUE;
1584 return sp;
1585}
1586
1587/*
1588 * Copy a state stack from "from" in b_sst_array[] to current_state;
1589 */
1590 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001591load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001592{
1593 int i;
1594 bufstate_T *bp;
1595
1596 clear_current_state();
1597 validate_current_state();
1598 keepend_level = -1;
1599 if (from->sst_stacksize
1600 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1601 {
1602 if (from->sst_stacksize > SST_FIX_STATES)
1603 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1604 else
1605 bp = from->sst_union.sst_stack;
1606 for (i = 0; i < from->sst_stacksize; ++i)
1607 {
1608 CUR_STATE(i).si_idx = bp[i].bs_idx;
1609 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001610#ifdef FEAT_CONCEAL
1611 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1612 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1613#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001614 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1615 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1616 keepend_level = i;
1617 CUR_STATE(i).si_ends = FALSE;
1618 CUR_STATE(i).si_m_lnum = 0;
1619 if (CUR_STATE(i).si_idx >= 0)
1620 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001621 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001622 else
1623 CUR_STATE(i).si_next_list = NULL;
1624 update_si_attr(i);
1625 }
1626 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001627 }
1628 current_next_list = from->sst_next_list;
1629 current_next_flags = from->sst_next_flags;
1630 current_lnum = from->sst_lnum;
1631}
1632
1633/*
1634 * Compare saved state stack "*sp" with the current state.
1635 * Return TRUE when they are equal.
1636 */
1637 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001638syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001639{
1640 int i, j;
1641 bufstate_T *bp;
1642 reg_extmatch_T *six, *bsx;
1643
1644 /* First a quick check if the stacks have the same size end nextlist. */
1645 if (sp->sst_stacksize == current_state.ga_len
1646 && sp->sst_next_list == current_next_list)
1647 {
1648 /* Need to compare all states on both stacks. */
1649 if (sp->sst_stacksize > SST_FIX_STATES)
1650 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1651 else
1652 bp = sp->sst_union.sst_stack;
1653
1654 for (i = current_state.ga_len; --i >= 0; )
1655 {
1656 /* If the item has another index the state is different. */
1657 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1658 break;
1659 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1660 {
1661 /* When the extmatch pointers are different, the strings in
1662 * them can still be the same. Check if the extmatch
1663 * references are equal. */
1664 bsx = bp[i].bs_extmatch;
1665 six = CUR_STATE(i).si_extmatch;
1666 /* If one of the extmatch pointers is NULL the states are
1667 * different. */
1668 if (bsx == NULL || six == NULL)
1669 break;
1670 for (j = 0; j < NSUBEXP; ++j)
1671 {
1672 /* Check each referenced match string. They must all be
1673 * equal. */
1674 if (bsx->matches[j] != six->matches[j])
1675 {
1676 /* If the pointer is different it can still be the
1677 * same text. Compare the strings, ignore case when
1678 * the start item has the sp_ic flag set. */
1679 if (bsx->matches[j] == NULL
1680 || six->matches[j] == NULL)
1681 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001682 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001683 ? MB_STRICMP(bsx->matches[j],
1684 six->matches[j]) != 0
1685 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1686 break;
1687 }
1688 }
1689 if (j != NSUBEXP)
1690 break;
1691 }
1692 }
1693 if (i < 0)
1694 return TRUE;
1695 }
1696 return FALSE;
1697}
1698
1699/*
1700 * We stop parsing syntax above line "lnum". If the stored state at or below
1701 * this line depended on a change before it, it now depends on the line below
1702 * the last parsed line.
1703 * The window looks like this:
1704 * line which changed
1705 * displayed line
1706 * displayed line
1707 * lnum -> line below window
1708 */
1709 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001710syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001711{
1712 synstate_T *sp;
1713
1714 sp = syn_stack_find_entry(lnum);
1715 if (sp != NULL && sp->sst_lnum < lnum)
1716 sp = sp->sst_next;
1717
1718 if (sp != NULL && sp->sst_change_lnum != 0)
1719 sp->sst_change_lnum = lnum;
1720}
1721
1722/*
1723 * End of handling of the state stack.
1724 ****************************************/
1725
1726 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001727invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001728{
1729 clear_current_state();
1730 current_state.ga_itemsize = 0; /* mark current_state invalid */
1731 current_next_list = NULL;
1732 keepend_level = -1;
1733}
1734
1735 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001736validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001737{
1738 current_state.ga_itemsize = sizeof(stateitem_T);
1739 current_state.ga_growsize = 3;
1740}
1741
1742/*
1743 * Return TRUE if the syntax at start of lnum changed since last time.
1744 * This will only be called just after get_syntax_attr() for the previous
1745 * line, to check if the next line needs to be redrawn too.
1746 */
1747 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001748syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001749{
1750 int retval = TRUE;
1751 synstate_T *sp;
1752
Bram Moolenaar071d4272004-06-13 20:20:40 +00001753 /*
1754 * Check the state stack when:
1755 * - lnum is just below the previously syntaxed line.
1756 * - lnum is not before the lines with saved states.
1757 * - lnum is not past the lines with saved states.
1758 * - lnum is at or before the last changed line.
1759 */
1760 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1761 {
1762 sp = syn_stack_find_entry(lnum);
1763 if (sp != NULL && sp->sst_lnum == lnum)
1764 {
1765 /*
1766 * finish the previous line (needed when not all of the line was
1767 * drawn)
1768 */
1769 (void)syn_finish_line(FALSE);
1770
1771 /*
1772 * Compare the current state with the previously saved state of
1773 * the line.
1774 */
1775 if (syn_stack_equal(sp))
1776 retval = FALSE;
1777
1778 /*
1779 * Store the current state in b_sst_array[] for later use.
1780 */
1781 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001782 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001783 }
1784 }
1785
Bram Moolenaar071d4272004-06-13 20:20:40 +00001786 return retval;
1787}
1788
1789/*
1790 * Finish the current line.
1791 * This doesn't return any attributes, it only gets the state at the end of
1792 * the line. It can start anywhere in the line, as long as the current state
1793 * is valid.
1794 */
1795 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001796syn_finish_line(
1797 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001798{
1799 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001800 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001801
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001802 while (!current_finished)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001803 {
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001804 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1805 /*
1806 * When syncing, and found some item, need to check the item.
1807 */
1808 if (syncing && current_state.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001809 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001810 /*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001811 * Check for match with sync item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001812 */
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001813 cur_si = &CUR_STATE(current_state.ga_len - 1);
1814 if (cur_si->si_idx >= 0
1815 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1816 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1817 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001818
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001819 /* syn_current_attr() will have skipped the check for an item
1820 * that ends here, need to do that now. Be careful not to go
1821 * past the NUL. */
1822 prev_current_col = current_col;
1823 if (syn_getcurline()[current_col] != NUL)
1824 ++current_col;
1825 check_state_ends();
1826 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001827 }
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001828 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001829 }
1830 return FALSE;
1831}
1832
1833/*
1834 * Return highlight attributes for next character.
1835 * Must first call syntax_start() once for the line.
1836 * "col" is normally 0 for the first use in a line, and increments by one each
1837 * time. It's allowed to skip characters and to stop before the end of the
1838 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001839 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1840 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001841 */
1842 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001843get_syntax_attr(
1844 colnr_T col,
1845 int *can_spell,
1846 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001847{
1848 int attr = 0;
1849
Bram Moolenaar349955a2007-08-14 21:07:36 +00001850 if (can_spell != NULL)
1851 /* Default: Only do spelling when there is no @Spell cluster or when
1852 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001853 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1854 ? (syn_block->b_spell_cluster_id == 0)
1855 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001856
Bram Moolenaar071d4272004-06-13 20:20:40 +00001857 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001858 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001859 return 0;
1860
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001861 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001862 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001863 {
1864 clear_current_state();
1865#ifdef FEAT_EVAL
1866 current_id = 0;
1867 current_trans_id = 0;
1868#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001869#ifdef FEAT_CONCEAL
1870 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001871 current_seqnr = 0;
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001872#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001873 return 0;
1874 }
1875
Bram Moolenaar071d4272004-06-13 20:20:40 +00001876 /* Make sure current_state is valid */
1877 if (INVALID_STATE(&current_state))
1878 validate_current_state();
1879
1880 /*
1881 * Skip from the current column to "col", get the attributes for "col".
1882 */
1883 while (current_col <= col)
1884 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001885 attr = syn_current_attr(FALSE, TRUE, can_spell,
1886 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001887 ++current_col;
1888 }
1889
Bram Moolenaar071d4272004-06-13 20:20:40 +00001890 return attr;
1891}
1892
1893/*
1894 * Get syntax attributes for current_lnum, current_col.
1895 */
1896 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001897syn_current_attr(
1898 int syncing, /* When 1: called for syncing */
1899 int displaying, /* result will be displayed */
1900 int *can_spell, /* return: do spell checking */
1901 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001902{
1903 int syn_id;
1904 lpos_T endpos; /* was: char_u *endp; */
1905 lpos_T hl_startpos; /* was: int hl_startcol; */
1906 lpos_T hl_endpos;
1907 lpos_T eos_pos; /* end-of-start match (start region) */
1908 lpos_T eoe_pos; /* end-of-end pattern */
1909 int end_idx; /* group ID for end pattern */
1910 int idx;
1911 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001912 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001913 int startcol;
1914 int endcol;
1915 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001916 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001917 short *next_list;
1918 int found_match; /* found usable match */
1919 static int try_next_column = FALSE; /* must try in next col */
1920 int do_keywords;
1921 regmmatch_T regmatch;
1922 lpos_T pos;
1923 int lc_col;
1924 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001925 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001926 char_u *line; /* current line. NOTE: becomes invalid after
1927 looking for a pattern match! */
1928
1929 /* variables for zero-width matches that have a "nextgroup" argument */
1930 int keep_next_list;
1931 int zero_width_next_list = FALSE;
1932 garray_T zero_width_next_ga;
1933
1934 /*
1935 * No character, no attributes! Past end of line?
1936 * Do try matching with an empty line (could be the start of a region).
1937 */
1938 line = syn_getcurline();
1939 if (line[current_col] == NUL && current_col != 0)
1940 {
1941 /*
1942 * If we found a match after the last column, use it.
1943 */
1944 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1945 && next_match_col != MAXCOL)
1946 (void)push_next_match(NULL);
1947
1948 current_finished = TRUE;
1949 current_state_stored = FALSE;
1950 return 0;
1951 }
1952
1953 /* if the current or next character is NUL, we will finish the line now */
1954 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1955 {
1956 current_finished = TRUE;
1957 current_state_stored = FALSE;
1958 }
1959
1960 /*
1961 * When in the previous column there was a match but it could not be used
1962 * (empty match or already matched in this column) need to try again in
1963 * the next column.
1964 */
1965 if (try_next_column)
1966 {
1967 next_match_idx = -1;
1968 try_next_column = FALSE;
1969 }
1970
1971 /* Only check for keywords when not syncing and there are some. */
1972 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001973 && (syn_block->b_keywtab.ht_used > 0
1974 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001975
1976 /* Init the list of zero-width matches with a nextlist. This is used to
1977 * avoid matching the same item in the same position twice. */
1978 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1979
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001980 /* use syntax iskeyword option */
1981 save_chartab(buf_chartab);
1982
Bram Moolenaar071d4272004-06-13 20:20:40 +00001983 /*
1984 * Repeat matching keywords and patterns, to find contained items at the
1985 * same column. This stops when there are no extra matches at the current
1986 * column.
1987 */
1988 do
1989 {
1990 found_match = FALSE;
1991 keep_next_list = FALSE;
1992 syn_id = 0;
1993
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001994
Bram Moolenaar071d4272004-06-13 20:20:40 +00001995 /*
1996 * 1. Check for a current state.
1997 * Only when there is no current state, or if the current state may
1998 * contain other things, we need to check for keywords and patterns.
1999 * Always need to check for contained items if some item has the
2000 * "containedin" argument (takes extra time!).
2001 */
2002 if (current_state.ga_len)
2003 cur_si = &CUR_STATE(current_state.ga_len - 1);
2004 else
2005 cur_si = NULL;
2006
Bram Moolenaar860cae12010-06-05 23:22:07 +02002007 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002008 || cur_si->si_cont_list != NULL)
2009 {
2010 /*
2011 * 2. Check for keywords, if on a keyword char after a non-keyword
2012 * char. Don't do this when syncing.
2013 */
2014 if (do_keywords)
2015 {
2016 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002017 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002018 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002019 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002020#ifdef FEAT_MBYTE
2021 - (has_mbyte
2022 ? (*mb_head_off)(line, line + current_col - 1)
2023 : 0)
2024#endif
2025 , syn_buf)))
2026 {
2027 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002028 &endcol, &flags, &next_list, cur_si,
2029 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002030 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002031 {
2032 if (push_current_state(KEYWORD_IDX) == OK)
2033 {
2034 cur_si = &CUR_STATE(current_state.ga_len - 1);
2035 cur_si->si_m_startcol = current_col;
2036 cur_si->si_h_startpos.lnum = current_lnum;
2037 cur_si->si_h_startpos.col = 0; /* starts right away */
2038 cur_si->si_m_endpos.lnum = current_lnum;
2039 cur_si->si_m_endpos.col = endcol;
2040 cur_si->si_h_endpos.lnum = current_lnum;
2041 cur_si->si_h_endpos.col = endcol;
2042 cur_si->si_ends = TRUE;
2043 cur_si->si_end_idx = 0;
2044 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002045#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002046 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002047 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002048 if (current_state.ga_len > 1)
2049 cur_si->si_flags |=
2050 CUR_STATE(current_state.ga_len - 2).si_flags
2051 & HL_CONCEAL;
2052#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002053 cur_si->si_id = syn_id;
2054 cur_si->si_trans_id = syn_id;
2055 if (flags & HL_TRANSP)
2056 {
2057 if (current_state.ga_len < 2)
2058 {
2059 cur_si->si_attr = 0;
2060 cur_si->si_trans_id = 0;
2061 }
2062 else
2063 {
2064 cur_si->si_attr = CUR_STATE(
2065 current_state.ga_len - 2).si_attr;
2066 cur_si->si_trans_id = CUR_STATE(
2067 current_state.ga_len - 2).si_trans_id;
2068 }
2069 }
2070 else
2071 cur_si->si_attr = syn_id2attr(syn_id);
2072 cur_si->si_cont_list = NULL;
2073 cur_si->si_next_list = next_list;
2074 check_keepend();
2075 }
2076 else
2077 vim_free(next_list);
2078 }
2079 }
2080 }
2081
2082 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002083 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002084 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002085 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002086 {
2087 /*
2088 * If we didn't check for a match yet, or we are past it, check
2089 * for any match with a pattern.
2090 */
2091 if (next_match_idx < 0 || next_match_col < (int)current_col)
2092 {
2093 /*
2094 * Check all relevant patterns for a match at this
2095 * position. This is complicated, because matching with a
2096 * pattern takes quite a bit of time, thus we want to
2097 * avoid doing it when it's not needed.
2098 */
2099 next_match_idx = 0; /* no match in this line yet */
2100 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002101 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002102 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002103 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002104 if ( spp->sp_syncing == syncing
2105 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2106 && (spp->sp_type == SPTYPE_MATCH
2107 || spp->sp_type == SPTYPE_START)
2108 && (current_next_list != NULL
2109 ? in_id_list(NULL, current_next_list,
2110 &spp->sp_syn, 0)
2111 : (cur_si == NULL
2112 ? !(spp->sp_flags & HL_CONTAINED)
2113 : in_id_list(cur_si,
2114 cur_si->si_cont_list, &spp->sp_syn,
2115 spp->sp_flags & HL_CONTAINED))))
2116 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002117 int r;
2118
Bram Moolenaar071d4272004-06-13 20:20:40 +00002119 /* If we already tried matching in this line, and
2120 * there isn't a match before next_match_col, skip
2121 * this item. */
2122 if (spp->sp_line_id == current_line_id
2123 && spp->sp_startcol >= next_match_col)
2124 continue;
2125 spp->sp_line_id = current_line_id;
2126
2127 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2128 if (lc_col < 0)
2129 lc_col = 0;
2130
2131 regmatch.rmm_ic = spp->sp_ic;
2132 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002133 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002134 current_lnum,
2135 (colnr_T)lc_col,
Bram Moolenaard23a8232018-02-10 18:45:26 +01002136 IF_SYN_TIME(&spp->sp_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002137 spp->sp_prog = regmatch.regprog;
2138 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002139 {
2140 /* no match in this line, try another one */
2141 spp->sp_startcol = MAXCOL;
2142 continue;
2143 }
2144
2145 /*
2146 * Compute the first column of the match.
2147 */
2148 syn_add_start_off(&pos, &regmatch,
2149 spp, SPO_MS_OFF, -1);
2150 if (pos.lnum > current_lnum)
2151 {
2152 /* must have used end of match in a next line,
2153 * we can't handle that */
2154 spp->sp_startcol = MAXCOL;
2155 continue;
2156 }
2157 startcol = pos.col;
2158
2159 /* remember the next column where this pattern
2160 * matches in the current line */
2161 spp->sp_startcol = startcol;
2162
2163 /*
2164 * If a previously found match starts at a lower
2165 * column number, don't use this one.
2166 */
2167 if (startcol >= next_match_col)
2168 continue;
2169
2170 /*
2171 * If we matched this pattern at this position
2172 * before, skip it. Must retry in the next
2173 * column, because it may match from there.
2174 */
2175 if (did_match_already(idx, &zero_width_next_ga))
2176 {
2177 try_next_column = TRUE;
2178 continue;
2179 }
2180
2181 endpos.lnum = regmatch.endpos[0].lnum;
2182 endpos.col = regmatch.endpos[0].col;
2183
2184 /* Compute the highlight start. */
2185 syn_add_start_off(&hl_startpos, &regmatch,
2186 spp, SPO_HS_OFF, -1);
2187
2188 /* Compute the region start. */
2189 /* Default is to use the end of the match. */
2190 syn_add_end_off(&eos_pos, &regmatch,
2191 spp, SPO_RS_OFF, 0);
2192
2193 /*
2194 * Grab the external submatches before they get
2195 * overwritten. Reference count doesn't change.
2196 */
2197 unref_extmatch(cur_extmatch);
2198 cur_extmatch = re_extmatch_out;
2199 re_extmatch_out = NULL;
2200
2201 flags = 0;
2202 eoe_pos.lnum = 0; /* avoid warning */
2203 eoe_pos.col = 0;
2204 end_idx = 0;
2205 hl_endpos.lnum = 0;
2206
2207 /*
2208 * For a "oneline" the end must be found in the
2209 * same line too. Search for it after the end of
2210 * the match with the start pattern. Set the
2211 * resulting end positions at the same time.
2212 */
2213 if (spp->sp_type == SPTYPE_START
2214 && (spp->sp_flags & HL_ONELINE))
2215 {
2216 lpos_T startpos;
2217
2218 startpos = endpos;
2219 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2220 &flags, &eoe_pos, &end_idx, cur_extmatch);
2221 if (endpos.lnum == 0)
2222 continue; /* not found */
2223 }
2224
2225 /*
2226 * For a "match" the size must be > 0 after the
2227 * end offset needs has been added. Except when
2228 * syncing.
2229 */
2230 else if (spp->sp_type == SPTYPE_MATCH)
2231 {
2232 syn_add_end_off(&hl_endpos, &regmatch, spp,
2233 SPO_HE_OFF, 0);
2234 syn_add_end_off(&endpos, &regmatch, spp,
2235 SPO_ME_OFF, 0);
2236 if (endpos.lnum == current_lnum
2237 && (int)endpos.col + syncing < startcol)
2238 {
2239 /*
2240 * If an empty string is matched, may need
2241 * to try matching again at next column.
2242 */
2243 if (regmatch.startpos[0].col
2244 == regmatch.endpos[0].col)
2245 try_next_column = TRUE;
2246 continue;
2247 }
2248 }
2249
2250 /*
2251 * keep the best match so far in next_match_*
2252 */
2253 /* Highlighting must start after startpos and end
2254 * before endpos. */
2255 if (hl_startpos.lnum == current_lnum
2256 && (int)hl_startpos.col < startcol)
2257 hl_startpos.col = startcol;
2258 limit_pos_zero(&hl_endpos, &endpos);
2259
2260 next_match_idx = idx;
2261 next_match_col = startcol;
2262 next_match_m_endpos = endpos;
2263 next_match_h_endpos = hl_endpos;
2264 next_match_h_startpos = hl_startpos;
2265 next_match_flags = flags;
2266 next_match_eos_pos = eos_pos;
2267 next_match_eoe_pos = eoe_pos;
2268 next_match_end_idx = end_idx;
2269 unref_extmatch(next_match_extmatch);
2270 next_match_extmatch = cur_extmatch;
2271 cur_extmatch = NULL;
2272 }
2273 }
2274 }
2275
2276 /*
2277 * If we found a match at the current column, use it.
2278 */
2279 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2280 {
2281 synpat_T *lspp;
2282
2283 /* When a zero-width item matched which has a nextgroup,
2284 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002285 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002286 if (next_match_m_endpos.lnum == current_lnum
2287 && next_match_m_endpos.col == current_col
2288 && lspp->sp_next_list != NULL)
2289 {
2290 current_next_list = lspp->sp_next_list;
2291 current_next_flags = lspp->sp_flags;
2292 keep_next_list = TRUE;
2293 zero_width_next_list = TRUE;
2294
2295 /* Add the index to a list, so that we can check
2296 * later that we don't match it again (and cause an
2297 * endless loop). */
2298 if (ga_grow(&zero_width_next_ga, 1) == OK)
2299 {
2300 ((int *)(zero_width_next_ga.ga_data))
2301 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002302 }
2303 next_match_idx = -1;
2304 }
2305 else
2306 cur_si = push_next_match(cur_si);
2307 found_match = TRUE;
2308 }
2309 }
2310 }
2311
2312 /*
2313 * Handle searching for nextgroup match.
2314 */
2315 if (current_next_list != NULL && !keep_next_list)
2316 {
2317 /*
2318 * If a nextgroup was not found, continue looking for one if:
2319 * - this is an empty line and the "skipempty" option was given
2320 * - we are on white space and the "skipwhite" option was given
2321 */
2322 if (!found_match)
2323 {
2324 line = syn_getcurline();
2325 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002326 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002327 || ((current_next_flags & HL_SKIPEMPTY)
2328 && *line == NUL))
2329 break;
2330 }
2331
2332 /*
2333 * If a nextgroup was found: Use it, and continue looking for
2334 * contained matches.
2335 * If a nextgroup was not found: Continue looking for a normal
2336 * match.
2337 * When did set current_next_list for a zero-width item and no
2338 * match was found don't loop (would get stuck).
2339 */
2340 current_next_list = NULL;
2341 next_match_idx = -1;
2342 if (!zero_width_next_list)
2343 found_match = TRUE;
2344 }
2345
2346 } while (found_match);
2347
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002348 restore_chartab(buf_chartab);
2349
Bram Moolenaar071d4272004-06-13 20:20:40 +00002350 /*
2351 * Use attributes from the current state, if within its highlighting.
2352 * If not, use attributes from the current-but-one state, etc.
2353 */
2354 current_attr = 0;
2355#ifdef FEAT_EVAL
2356 current_id = 0;
2357 current_trans_id = 0;
2358#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002359#ifdef FEAT_CONCEAL
2360 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02002361 current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002362#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002363 if (cur_si != NULL)
2364 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002365#ifndef FEAT_EVAL
2366 int current_trans_id = 0;
2367#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002368 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2369 {
2370 sip = &CUR_STATE(idx);
2371 if ((current_lnum > sip->si_h_startpos.lnum
2372 || (current_lnum == sip->si_h_startpos.lnum
2373 && current_col >= sip->si_h_startpos.col))
2374 && (sip->si_h_endpos.lnum == 0
2375 || current_lnum < sip->si_h_endpos.lnum
2376 || (current_lnum == sip->si_h_endpos.lnum
2377 && current_col < sip->si_h_endpos.col)))
2378 {
2379 current_attr = sip->si_attr;
2380#ifdef FEAT_EVAL
2381 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002382#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002383 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002384#ifdef FEAT_CONCEAL
2385 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002386 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002387 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002388#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002389 break;
2390 }
2391 }
2392
Bram Moolenaar217ad922005-03-20 22:37:15 +00002393 if (can_spell != NULL)
2394 {
2395 struct sp_syn sps;
2396
2397 /*
2398 * set "can_spell" to TRUE if spell checking is supposed to be
2399 * done in the current item.
2400 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002401 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002402 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002403 /* There is no @Spell cluster: Do spelling for items without
2404 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002405 if (syn_block->b_nospell_cluster_id == 0
2406 || current_trans_id == 0)
2407 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002408 else
2409 {
2410 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002411 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002412 sps.cont_in_list = NULL;
2413 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2414 }
2415 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002416 else
2417 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002418 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002419 * the @Spell cluster. But not when @NoSpell is also there.
2420 * At the toplevel only spell check when ":syn spell toplevel"
2421 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002422 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002423 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002424 else
2425 {
2426 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002427 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002428 sps.cont_in_list = NULL;
2429 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2430
Bram Moolenaar860cae12010-06-05 23:22:07 +02002431 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002432 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002433 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002434 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2435 *can_spell = FALSE;
2436 }
2437 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002438 }
2439 }
2440
2441
Bram Moolenaar071d4272004-06-13 20:20:40 +00002442 /*
2443 * Check for end of current state (and the states before it) at the
2444 * next column. Don't do this for syncing, because we would miss a
2445 * single character match.
2446 * First check if the current state ends at the current column. It
2447 * may be for an empty match and a containing item might end in the
2448 * current column.
2449 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002450 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002451 {
2452 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002453 if (current_state.ga_len > 0
2454 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002455 {
2456 ++current_col;
2457 check_state_ends();
2458 --current_col;
2459 }
2460 }
2461 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002462 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002463 /* Default: Only do spelling when there is no @Spell cluster or when
2464 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002465 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2466 ? (syn_block->b_spell_cluster_id == 0)
2467 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002468
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002469 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002470 if (current_next_list != NULL
2471 && syn_getcurline()[current_col + 1] == NUL
2472 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2473 current_next_list = NULL;
2474
2475 if (zero_width_next_ga.ga_len > 0)
2476 ga_clear(&zero_width_next_ga);
2477
2478 /* No longer need external matches. But keep next_match_extmatch. */
2479 unref_extmatch(re_extmatch_out);
2480 re_extmatch_out = NULL;
2481 unref_extmatch(cur_extmatch);
2482
2483 return current_attr;
2484}
2485
2486
2487/*
2488 * Check if we already matched pattern "idx" at the current column.
2489 */
2490 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002491did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002492{
2493 int i;
2494
2495 for (i = current_state.ga_len; --i >= 0; )
2496 if (CUR_STATE(i).si_m_startcol == (int)current_col
2497 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2498 && CUR_STATE(i).si_idx == idx)
2499 return TRUE;
2500
2501 /* Zero-width matches with a nextgroup argument are not put on the syntax
2502 * stack, and can only be matched once anyway. */
2503 for (i = gap->ga_len; --i >= 0; )
2504 if (((int *)(gap->ga_data))[i] == idx)
2505 return TRUE;
2506
2507 return FALSE;
2508}
2509
2510/*
2511 * Push the next match onto the stack.
2512 */
2513 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002514push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002515{
2516 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002517#ifdef FEAT_CONCEAL
2518 int save_flags;
2519#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002520
Bram Moolenaar860cae12010-06-05 23:22:07 +02002521 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002522
2523 /*
2524 * Push the item in current_state stack;
2525 */
2526 if (push_current_state(next_match_idx) == OK)
2527 {
2528 /*
2529 * If it's a start-skip-end type that crosses lines, figure out how
2530 * much it continues in this line. Otherwise just fill in the length.
2531 */
2532 cur_si = &CUR_STATE(current_state.ga_len - 1);
2533 cur_si->si_h_startpos = next_match_h_startpos;
2534 cur_si->si_m_startcol = current_col;
2535 cur_si->si_m_lnum = current_lnum;
2536 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002537#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002538 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002539 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002540 if (current_state.ga_len > 1)
2541 cur_si->si_flags |=
2542 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2543#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002544 cur_si->si_next_list = spp->sp_next_list;
2545 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2546 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2547 {
2548 /* Try to find the end pattern in the current line */
2549 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2550 check_keepend();
2551 }
2552 else
2553 {
2554 cur_si->si_m_endpos = next_match_m_endpos;
2555 cur_si->si_h_endpos = next_match_h_endpos;
2556 cur_si->si_ends = TRUE;
2557 cur_si->si_flags |= next_match_flags;
2558 cur_si->si_eoe_pos = next_match_eoe_pos;
2559 cur_si->si_end_idx = next_match_end_idx;
2560 }
2561 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2562 keepend_level = current_state.ga_len - 1;
2563 check_keepend();
2564 update_si_attr(current_state.ga_len - 1);
2565
Bram Moolenaar860cae12010-06-05 23:22:07 +02002566#ifdef FEAT_CONCEAL
2567 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2568#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002569 /*
2570 * If the start pattern has another highlight group, push another item
2571 * on the stack for the start pattern.
2572 */
2573 if ( spp->sp_type == SPTYPE_START
2574 && spp->sp_syn_match_id != 0
2575 && push_current_state(next_match_idx) == OK)
2576 {
2577 cur_si = &CUR_STATE(current_state.ga_len - 1);
2578 cur_si->si_h_startpos = next_match_h_startpos;
2579 cur_si->si_m_startcol = current_col;
2580 cur_si->si_m_lnum = current_lnum;
2581 cur_si->si_m_endpos = next_match_eos_pos;
2582 cur_si->si_h_endpos = next_match_eos_pos;
2583 cur_si->si_ends = TRUE;
2584 cur_si->si_end_idx = 0;
2585 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002586#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002587 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002588 cur_si->si_flags |= save_flags;
2589 if (cur_si->si_flags & HL_CONCEALENDS)
2590 cur_si->si_flags |= HL_CONCEAL;
2591#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002592 cur_si->si_next_list = NULL;
2593 check_keepend();
2594 update_si_attr(current_state.ga_len - 1);
2595 }
2596 }
2597
2598 next_match_idx = -1; /* try other match next time */
2599
2600 return cur_si;
2601}
2602
2603/*
2604 * Check for end of current state (and the states before it).
2605 */
2606 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002607check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002608{
2609 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002610 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002611
2612 cur_si = &CUR_STATE(current_state.ga_len - 1);
2613 for (;;)
2614 {
2615 if (cur_si->si_ends
2616 && (cur_si->si_m_endpos.lnum < current_lnum
2617 || (cur_si->si_m_endpos.lnum == current_lnum
2618 && cur_si->si_m_endpos.col <= current_col)))
2619 {
2620 /*
2621 * If there is an end pattern group ID, highlight the end pattern
2622 * now. No need to pop the current item from the stack.
2623 * Only do this if the end pattern continues beyond the current
2624 * position.
2625 */
2626 if (cur_si->si_end_idx
2627 && (cur_si->si_eoe_pos.lnum > current_lnum
2628 || (cur_si->si_eoe_pos.lnum == current_lnum
2629 && cur_si->si_eoe_pos.col > current_col)))
2630 {
2631 cur_si->si_idx = cur_si->si_end_idx;
2632 cur_si->si_end_idx = 0;
2633 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2634 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2635 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002636#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002637 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002638 if (cur_si->si_flags & HL_CONCEALENDS)
2639 cur_si->si_flags |= HL_CONCEAL;
2640#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002641 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002642
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002643 /* nextgroup= should not match in the end pattern */
2644 current_next_list = NULL;
2645
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002646 /* what matches next may be different now, clear it */
2647 next_match_idx = 0;
2648 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002649 break;
2650 }
2651 else
2652 {
2653 /* handle next_list, unless at end of line and no "skipnl" or
2654 * "skipempty" */
2655 current_next_list = cur_si->si_next_list;
2656 current_next_flags = cur_si->si_flags;
2657 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2658 && syn_getcurline()[current_col] == NUL)
2659 current_next_list = NULL;
2660
2661 /* When the ended item has "extend", another item with
2662 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002663 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002664
2665 pop_current_state();
2666
2667 if (current_state.ga_len == 0)
2668 break;
2669
Bram Moolenaar81993f42008-01-11 20:27:45 +00002670 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002671 {
2672 syn_update_ends(FALSE);
2673 if (current_state.ga_len == 0)
2674 break;
2675 }
2676
2677 cur_si = &CUR_STATE(current_state.ga_len - 1);
2678
2679 /*
2680 * Only for a region the search for the end continues after
2681 * the end of the contained item. If the contained match
2682 * included the end-of-line, break here, the region continues.
2683 * Don't do this when:
2684 * - "keepend" is used for the contained item
2685 * - not at the end of the line (could be end="x$"me=e-1).
2686 * - "excludenl" is used (HL_HAS_EOL won't be set)
2687 */
2688 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002689 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002690 == SPTYPE_START
2691 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2692 {
2693 update_si_end(cur_si, (int)current_col, TRUE);
2694 check_keepend();
2695 if ((current_next_flags & HL_HAS_EOL)
2696 && keepend_level < 0
2697 && syn_getcurline()[current_col] == NUL)
2698 break;
2699 }
2700 }
2701 }
2702 else
2703 break;
2704 }
2705}
2706
2707/*
2708 * Update an entry in the current_state stack for a match or region. This
2709 * fills in si_attr, si_next_list and si_cont_list.
2710 */
2711 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002712update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002713{
2714 stateitem_T *sip = &CUR_STATE(idx);
2715 synpat_T *spp;
2716
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002717 /* This should not happen... */
2718 if (sip->si_idx < 0)
2719 return;
2720
Bram Moolenaar860cae12010-06-05 23:22:07 +02002721 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002722 if (sip->si_flags & HL_MATCH)
2723 sip->si_id = spp->sp_syn_match_id;
2724 else
2725 sip->si_id = spp->sp_syn.id;
2726 sip->si_attr = syn_id2attr(sip->si_id);
2727 sip->si_trans_id = sip->si_id;
2728 if (sip->si_flags & HL_MATCH)
2729 sip->si_cont_list = NULL;
2730 else
2731 sip->si_cont_list = spp->sp_cont_list;
2732
2733 /*
2734 * For transparent items, take attr from outer item.
2735 * Also take cont_list, if there is none.
2736 * Don't do this for the matchgroup of a start or end pattern.
2737 */
2738 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2739 {
2740 if (idx == 0)
2741 {
2742 sip->si_attr = 0;
2743 sip->si_trans_id = 0;
2744 if (sip->si_cont_list == NULL)
2745 sip->si_cont_list = ID_LIST_ALL;
2746 }
2747 else
2748 {
2749 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2750 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002751 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2752 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002753 if (sip->si_cont_list == NULL)
2754 {
2755 sip->si_flags |= HL_TRANS_CONT;
2756 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2757 }
2758 }
2759 }
2760}
2761
2762/*
2763 * Check the current stack for patterns with "keepend" flag.
2764 * Propagate the match-end to contained items, until a "skipend" item is found.
2765 */
2766 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002767check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002768{
2769 int i;
2770 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002771 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002772 stateitem_T *sip;
2773
2774 /*
2775 * This check can consume a lot of time; only do it from the level where
2776 * there really is a keepend.
2777 */
2778 if (keepend_level < 0)
2779 return;
2780
2781 /*
2782 * Find the last index of an "extend" item. "keepend" items before that
2783 * won't do anything. If there is no "extend" item "i" will be
2784 * "keepend_level" and all "keepend" items will work normally.
2785 */
2786 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2787 if (CUR_STATE(i).si_flags & HL_EXTEND)
2788 break;
2789
2790 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002791 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002792 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002793 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002794 for ( ; i < current_state.ga_len; ++i)
2795 {
2796 sip = &CUR_STATE(i);
2797 if (maxpos.lnum != 0)
2798 {
2799 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002800 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002801 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2802 sip->si_ends = TRUE;
2803 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002804 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2805 {
2806 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002807 || maxpos.lnum > sip->si_m_endpos.lnum
2808 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002809 && maxpos.col > sip->si_m_endpos.col))
2810 maxpos = sip->si_m_endpos;
2811 if (maxpos_h.lnum == 0
2812 || maxpos_h.lnum > sip->si_h_endpos.lnum
2813 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2814 && maxpos_h.col > sip->si_h_endpos.col))
2815 maxpos_h = sip->si_h_endpos;
2816 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002817 }
2818}
2819
2820/*
2821 * Update an entry in the current_state stack for a start-skip-end pattern.
2822 * This finds the end of the current item, if it's in the current line.
2823 *
2824 * Return the flags for the matched END.
2825 */
2826 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002827update_si_end(
2828 stateitem_T *sip,
2829 int startcol, /* where to start searching for the end */
2830 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002831{
2832 lpos_T startpos;
2833 lpos_T endpos;
2834 lpos_T hl_endpos;
2835 lpos_T end_endpos;
2836 int end_idx;
2837
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002838 /* return quickly for a keyword */
2839 if (sip->si_idx < 0)
2840 return;
2841
Bram Moolenaar071d4272004-06-13 20:20:40 +00002842 /* Don't update when it's already done. Can be a match of an end pattern
2843 * that started in a previous line. Watch out: can also be a "keepend"
2844 * from a containing item. */
2845 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2846 return;
2847
2848 /*
2849 * We need to find the end of the region. It may continue in the next
2850 * line.
2851 */
2852 end_idx = 0;
2853 startpos.lnum = current_lnum;
2854 startpos.col = startcol;
2855 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2856 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2857
2858 if (endpos.lnum == 0)
2859 {
2860 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002861 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002862 {
2863 /* a "oneline" never continues in the next line */
2864 sip->si_ends = TRUE;
2865 sip->si_m_endpos.lnum = current_lnum;
2866 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2867 }
2868 else
2869 {
2870 /* continues in the next line */
2871 sip->si_ends = FALSE;
2872 sip->si_m_endpos.lnum = 0;
2873 }
2874 sip->si_h_endpos = sip->si_m_endpos;
2875 }
2876 else
2877 {
2878 /* match within this line */
2879 sip->si_m_endpos = endpos;
2880 sip->si_h_endpos = hl_endpos;
2881 sip->si_eoe_pos = end_endpos;
2882 sip->si_ends = TRUE;
2883 sip->si_end_idx = end_idx;
2884 }
2885}
2886
2887/*
2888 * Add a new state to the current state stack.
2889 * It is cleared and the index set to "idx".
2890 * Return FAIL if it's not possible (out of memory).
2891 */
2892 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002893push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002894{
2895 if (ga_grow(&current_state, 1) == FAIL)
2896 return FAIL;
2897 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2898 CUR_STATE(current_state.ga_len).si_idx = idx;
2899 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002900 return OK;
2901}
2902
2903/*
2904 * Remove a state from the current_state stack.
2905 */
2906 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002907pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002908{
2909 if (current_state.ga_len)
2910 {
2911 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2912 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002913 }
2914 /* after the end of a pattern, try matching a keyword or pattern */
2915 next_match_idx = -1;
2916
2917 /* if first state with "keepend" is popped, reset keepend_level */
2918 if (keepend_level >= current_state.ga_len)
2919 keepend_level = -1;
2920}
2921
2922/*
2923 * Find the end of a start/skip/end syntax region after "startpos".
2924 * Only checks one line.
2925 * Also handles a match item that continued from a previous line.
2926 * If not found, the syntax item continues in the next line. m_endpos->lnum
2927 * will be 0.
2928 * If found, the end of the region and the end of the highlighting is
2929 * computed.
2930 */
2931 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002932find_endpos(
2933 int idx, /* index of the pattern */
2934 lpos_T *startpos, /* where to start looking for an END match */
2935 lpos_T *m_endpos, /* return: end of match */
2936 lpos_T *hl_endpos, /* return: end of highlighting */
2937 long *flagsp, /* return: flags of matching END */
2938 lpos_T *end_endpos, /* return: end of end pattern match */
2939 int *end_idx, /* return: group ID for end pat. match, or 0 */
2940 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002941{
2942 colnr_T matchcol;
2943 synpat_T *spp, *spp_skip;
2944 int start_idx;
2945 int best_idx;
2946 regmmatch_T regmatch;
2947 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2948 lpos_T pos;
2949 char_u *line;
2950 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002951 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002952
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002953 /* just in case we are invoked for a keyword */
2954 if (idx < 0)
2955 return;
2956
Bram Moolenaar071d4272004-06-13 20:20:40 +00002957 /*
2958 * Check for being called with a START pattern.
2959 * Can happen with a match that continues to the next line, because it
2960 * contained a region.
2961 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002962 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002963 if (spp->sp_type != SPTYPE_START)
2964 {
2965 *hl_endpos = *startpos;
2966 return;
2967 }
2968
2969 /*
2970 * Find the SKIP or first END pattern after the last START pattern.
2971 */
2972 for (;;)
2973 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002974 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002975 if (spp->sp_type != SPTYPE_START)
2976 break;
2977 ++idx;
2978 }
2979
2980 /*
2981 * Lookup the SKIP pattern (if present)
2982 */
2983 if (spp->sp_type == SPTYPE_SKIP)
2984 {
2985 spp_skip = spp;
2986 ++idx;
2987 }
2988 else
2989 spp_skip = NULL;
2990
2991 /* Setup external matches for syn_regexec(). */
2992 unref_extmatch(re_extmatch_in);
2993 re_extmatch_in = ref_extmatch(start_ext);
2994
2995 matchcol = startpos->col; /* start looking for a match at sstart */
2996 start_idx = idx; /* remember the first END pattern. */
2997 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002998
2999 /* use syntax iskeyword option */
3000 save_chartab(buf_chartab);
3001
Bram Moolenaar071d4272004-06-13 20:20:40 +00003002 for (;;)
3003 {
3004 /*
3005 * Find end pattern that matches first after "matchcol".
3006 */
3007 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003008 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003009 {
3010 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003011 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003012
Bram Moolenaar860cae12010-06-05 23:22:07 +02003013 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003014 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
3015 break;
3016 lc_col -= spp->sp_offsets[SPO_LC_OFF];
3017 if (lc_col < 0)
3018 lc_col = 0;
3019
3020 regmatch.rmm_ic = spp->sp_ic;
3021 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003022 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3023 IF_SYN_TIME(&spp->sp_time));
3024 spp->sp_prog = regmatch.regprog;
3025 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003026 {
3027 if (best_idx == -1 || regmatch.startpos[0].col
3028 < best_regmatch.startpos[0].col)
3029 {
3030 best_idx = idx;
3031 best_regmatch.startpos[0] = regmatch.startpos[0];
3032 best_regmatch.endpos[0] = regmatch.endpos[0];
3033 }
3034 }
3035 }
3036
3037 /*
3038 * If all end patterns have been tried, and there is no match, the
3039 * item continues until end-of-line.
3040 */
3041 if (best_idx == -1)
3042 break;
3043
3044 /*
3045 * If the skip pattern matches before the end pattern,
3046 * continue searching after the skip pattern.
3047 */
3048 if (spp_skip != NULL)
3049 {
3050 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003051 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003052
3053 if (lc_col < 0)
3054 lc_col = 0;
3055 regmatch.rmm_ic = spp_skip->sp_ic;
3056 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003057 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3058 IF_SYN_TIME(&spp_skip->sp_time));
3059 spp_skip->sp_prog = regmatch.regprog;
3060 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003061 <= best_regmatch.startpos[0].col)
3062 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003063 int line_len;
3064
Bram Moolenaar071d4272004-06-13 20:20:40 +00003065 /* Add offset to skip pattern match */
3066 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3067
3068 /* If the skip pattern goes on to the next line, there is no
3069 * match with an end pattern in this line. */
3070 if (pos.lnum > startpos->lnum)
3071 break;
3072
3073 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003074 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003075
3076 /* take care of an empty match or negative offset */
3077 if (pos.col <= matchcol)
3078 ++matchcol;
3079 else if (pos.col <= regmatch.endpos[0].col)
3080 matchcol = pos.col;
3081 else
3082 /* Be careful not to jump over the NUL at the end-of-line */
3083 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003084 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003085 ++matchcol)
3086 ;
3087
3088 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003089 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003090 break;
3091
3092 continue; /* start with first end pattern again */
3093 }
3094 }
3095
3096 /*
3097 * Match from start pattern to end pattern.
3098 * Correct for match and highlight offset of end pattern.
3099 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003100 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003101 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3102 /* can't end before the start */
3103 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3104 m_endpos->col = startpos->col;
3105
3106 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3107 /* can't end before the start */
3108 if (end_endpos->lnum == startpos->lnum
3109 && end_endpos->col < startpos->col)
3110 end_endpos->col = startpos->col;
3111 /* can't end after the match */
3112 limit_pos(end_endpos, m_endpos);
3113
3114 /*
3115 * If the end group is highlighted differently, adjust the pointers.
3116 */
3117 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3118 {
3119 *end_idx = best_idx;
3120 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3121 {
3122 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3123 hl_endpos->col = best_regmatch.endpos[0].col;
3124 }
3125 else
3126 {
3127 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3128 hl_endpos->col = best_regmatch.startpos[0].col;
3129 }
3130 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3131
3132 /* can't end before the start */
3133 if (hl_endpos->lnum == startpos->lnum
3134 && hl_endpos->col < startpos->col)
3135 hl_endpos->col = startpos->col;
3136 limit_pos(hl_endpos, m_endpos);
3137
3138 /* now the match ends where the highlighting ends, it is turned
3139 * into the matchgroup for the end */
3140 *m_endpos = *hl_endpos;
3141 }
3142 else
3143 {
3144 *end_idx = 0;
3145 *hl_endpos = *end_endpos;
3146 }
3147
3148 *flagsp = spp->sp_flags;
3149
3150 had_match = TRUE;
3151 break;
3152 }
3153
3154 /* no match for an END pattern in this line */
3155 if (!had_match)
3156 m_endpos->lnum = 0;
3157
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003158 restore_chartab(buf_chartab);
3159
Bram Moolenaar071d4272004-06-13 20:20:40 +00003160 /* Remove external matches. */
3161 unref_extmatch(re_extmatch_in);
3162 re_extmatch_in = NULL;
3163}
3164
3165/*
3166 * Limit "pos" not to be after "limit".
3167 */
3168 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003169limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003170{
3171 if (pos->lnum > limit->lnum)
3172 *pos = *limit;
3173 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3174 pos->col = limit->col;
3175}
3176
3177/*
3178 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3179 */
3180 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003181limit_pos_zero(
3182 lpos_T *pos,
3183 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003184{
3185 if (pos->lnum == 0)
3186 *pos = *limit;
3187 else
3188 limit_pos(pos, limit);
3189}
3190
3191/*
3192 * Add offset to matched text for end of match or highlight.
3193 */
3194 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003195syn_add_end_off(
3196 lpos_T *result, /* returned position */
3197 regmmatch_T *regmatch, /* start/end of match */
3198 synpat_T *spp, /* matched pattern */
3199 int idx, /* index of offset */
3200 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003201{
3202 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003203 int off;
3204 char_u *base;
3205 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003206
3207 if (spp->sp_off_flags & (1 << idx))
3208 {
3209 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003210 col = regmatch->startpos[0].col;
3211 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003212 }
3213 else
3214 {
3215 result->lnum = regmatch->endpos[0].lnum;
3216 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003217 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003218 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003219 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3220 * is a matchgroup. Watch out for match with last NL in the buffer. */
3221 if (result->lnum > syn_buf->b_ml.ml_line_count)
3222 col = 0;
3223 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003224 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003225 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3226 p = base + col;
3227 if (off > 0)
3228 {
3229 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003230 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003231 }
3232 else if (off < 0)
3233 {
3234 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003235 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003236 }
3237 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003238 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003239 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003240}
3241
3242/*
3243 * Add offset to matched text for start of match or highlight.
3244 * Avoid resulting column to become negative.
3245 */
3246 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003247syn_add_start_off(
3248 lpos_T *result, /* returned position */
3249 regmmatch_T *regmatch, /* start/end of match */
3250 synpat_T *spp,
3251 int idx,
3252 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003253{
3254 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003255 int off;
3256 char_u *base;
3257 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003258
3259 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3260 {
3261 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003262 col = regmatch->endpos[0].col;
3263 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003264 }
3265 else
3266 {
3267 result->lnum = regmatch->startpos[0].lnum;
3268 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003269 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003270 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003271 if (result->lnum > syn_buf->b_ml.ml_line_count)
3272 {
3273 /* a "\n" at the end of the pattern may take us below the last line */
3274 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003275 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003276 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003277 if (off != 0)
3278 {
3279 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3280 p = base + col;
3281 if (off > 0)
3282 {
3283 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003284 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003285 }
3286 else if (off < 0)
3287 {
3288 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003289 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003290 }
3291 col = (int)(p - base);
3292 }
3293 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003294}
3295
3296/*
3297 * Get current line in syntax buffer.
3298 */
3299 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003300syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003301{
3302 return ml_get_buf(syn_buf, current_lnum, FALSE);
3303}
3304
3305/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003306 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003307 * Returns TRUE when there is a match.
3308 */
3309 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003310syn_regexec(
3311 regmmatch_T *rmp,
3312 linenr_T lnum,
3313 colnr_T col,
3314 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003315{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003316 int r;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003317#ifdef FEAT_RELTIME
3318 int timed_out = FALSE;
3319#endif
Bram Moolenaarf7512552013-06-06 14:55:19 +02003320#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003321 proftime_T pt;
3322
3323 if (syn_time_on)
3324 profile_start(&pt);
3325#endif
3326
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003327 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003328 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
3329#ifdef FEAT_RELTIME
3330 syn_tm, &timed_out
3331#else
3332 NULL, NULL
3333#endif
3334 );
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003335
Bram Moolenaarf7512552013-06-06 14:55:19 +02003336#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003337 if (syn_time_on)
3338 {
3339 profile_end(&pt);
3340 profile_add(&st->total, &pt);
3341 if (profile_cmp(&pt, &st->slowest) < 0)
3342 st->slowest = pt;
3343 ++st->count;
3344 if (r > 0)
3345 ++st->match;
3346 }
3347#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003348#ifdef FEAT_RELTIME
3349 if (timed_out)
3350 syn_win->w_s->b_syn_slow = TRUE;
3351#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003352
3353 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003354 {
3355 rmp->startpos[0].lnum += lnum;
3356 rmp->endpos[0].lnum += lnum;
3357 return TRUE;
3358 }
3359 return FALSE;
3360}
3361
3362/*
3363 * Check one position in a line for a matching keyword.
3364 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003365 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003366 */
3367 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003368check_keyword_id(
3369 char_u *line,
3370 int startcol, /* position in line to check for keyword */
3371 int *endcolp, /* return: character after found keyword */
3372 long *flagsp, /* return: flags of matching keyword */
3373 short **next_listp, /* return: next_list of matching keyword */
3374 stateitem_T *cur_si, /* item at the top of the stack */
3375 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003376{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003377 keyentry_T *kp;
3378 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003379 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003380 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003381 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003382 hashtab_T *ht;
3383 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003384
3385 /* Find first character after the keyword. First character was already
3386 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003387 kwp = line + startcol;
3388 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003389 do
3390 {
3391#ifdef FEAT_MBYTE
3392 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003393 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003394 else
3395#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003396 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003397 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003398 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003399
Bram Moolenaardad6b692005-01-25 22:14:34 +00003400 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003401 return 0;
3402
3403 /*
3404 * Must make a copy of the keyword, so we can add a NUL and make it
3405 * lowercase.
3406 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003407 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003408
3409 /*
3410 * Try twice:
3411 * 1. matching case
3412 * 2. ignoring case
3413 */
3414 for (round = 1; round <= 2; ++round)
3415 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003416 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003417 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003418 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003419 if (round == 2) /* ignore case */
3420 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003421
3422 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003423 * Find keywords that match. There can be several with different
3424 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003425 * When current_next_list is non-zero accept only that group, otherwise:
3426 * Accept a not-contained keyword at toplevel.
3427 * Accept a keyword at other levels only if it is in the contains list.
3428 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003429 hi = hash_find(ht, keyword);
3430 if (!HASHITEM_EMPTY(hi))
3431 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003432 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003433 if (current_next_list != 0
3434 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3435 : (cur_si == NULL
3436 ? !(kp->flags & HL_CONTAINED)
3437 : in_id_list(cur_si, cur_si->si_cont_list,
3438 &kp->k_syn, kp->flags & HL_CONTAINED)))
3439 {
3440 *endcolp = startcol + kwlen;
3441 *flagsp = kp->flags;
3442 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003443#ifdef FEAT_CONCEAL
3444 *ccharp = kp->k_char;
3445#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003446 return kp->k_syn.id;
3447 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003448 }
3449 }
3450 return 0;
3451}
3452
3453/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003454 * Handle ":syntax conceal" command.
3455 */
3456 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003457syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003458{
3459#ifdef FEAT_CONCEAL
3460 char_u *arg = eap->arg;
3461 char_u *next;
3462
3463 eap->nextcmd = find_nextcmd(arg);
3464 if (eap->skip)
3465 return;
3466
3467 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003468 if (*arg == NUL)
3469 {
3470 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar83064062017-06-13 16:34:54 +02003471 MSG(_("syntax conceal on"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003472 else
Bram Moolenaar83064062017-06-13 16:34:54 +02003473 MSG(_("syntax conceal off"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003474 }
3475 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003476 curwin->w_s->b_syn_conceal = TRUE;
3477 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3478 curwin->w_s->b_syn_conceal = FALSE;
3479 else
3480 EMSG2(_("E390: Illegal argument: %s"), arg);
3481#endif
3482}
3483
3484/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003485 * Handle ":syntax case" command.
3486 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003487 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003488syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003489{
3490 char_u *arg = eap->arg;
3491 char_u *next;
3492
3493 eap->nextcmd = find_nextcmd(arg);
3494 if (eap->skip)
3495 return;
3496
3497 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003498 if (*arg == NUL)
3499 {
3500 if (curwin->w_s->b_syn_ic)
3501 MSG(_("syntax case ignore"));
3502 else
3503 MSG(_("syntax case match"));
3504 }
3505 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003506 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003507 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003508 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003509 else
3510 EMSG2(_("E390: Illegal argument: %s"), arg);
3511}
3512
3513/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003514 * Handle ":syntax spell" command.
3515 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003516 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003517syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003518{
3519 char_u *arg = eap->arg;
3520 char_u *next;
3521
3522 eap->nextcmd = find_nextcmd(arg);
3523 if (eap->skip)
3524 return;
3525
3526 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003527 if (*arg == NUL)
3528 {
3529 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
3530 MSG(_("syntax spell toplevel"));
3531 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
3532 MSG(_("syntax spell notoplevel"));
3533 else
3534 MSG(_("syntax spell default"));
3535 }
3536 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003537 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003538 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003539 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003540 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003541 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003542 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003543 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003544 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003545 return;
3546 }
3547
3548 /* assume spell checking changed, force a redraw */
3549 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003550}
3551
3552/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003553 * Handle ":syntax iskeyword" command.
3554 */
3555 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003556syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003557{
3558 char_u *arg = eap->arg;
3559 char_u save_chartab[32];
3560 char_u *save_isk;
3561
3562 if (eap->skip)
3563 return;
3564
3565 arg = skipwhite(arg);
3566 if (*arg == NUL)
3567 {
3568 MSG_PUTS("\n");
3569 MSG_PUTS(_("syntax iskeyword "));
3570 if (curwin->w_s->b_syn_isk != empty_option)
3571 msg_outtrans(curwin->w_s->b_syn_isk);
3572 else
3573 msg_outtrans((char_u *)"not set");
3574 }
3575 else
3576 {
3577 if (STRNICMP(arg, "clear", 5) == 0)
3578 {
3579 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3580 (size_t)32);
3581 clear_string_option(&curwin->w_s->b_syn_isk);
3582 }
3583 else
3584 {
3585 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3586 save_isk = curbuf->b_p_isk;
3587 curbuf->b_p_isk = vim_strsave(arg);
3588
3589 buf_init_chartab(curbuf, FALSE);
3590 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3591 (size_t)32);
3592 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3593 clear_string_option(&curwin->w_s->b_syn_isk);
3594 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3595 curbuf->b_p_isk = save_isk;
3596 }
3597 }
3598 redraw_win_later(curwin, NOT_VALID);
3599}
3600
3601/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003602 * Clear all syntax info for one buffer.
3603 */
3604 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003605syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003606{
3607 int i;
3608
Bram Moolenaar860cae12010-06-05 23:22:07 +02003609 block->b_syn_error = FALSE; /* clear previous error */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003610#ifdef FEAT_RELTIME
3611 block->b_syn_slow = FALSE; /* clear previous timeout */
3612#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003613 block->b_syn_ic = FALSE; /* Use case, by default */
3614 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3615 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003616#ifdef FEAT_CONCEAL
3617 block->b_syn_conceal = FALSE;
3618#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003619
3620 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003621 clear_keywtab(&block->b_keywtab);
3622 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003623
3624 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003625 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3626 syn_clear_pattern(block, i);
3627 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003628
3629 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003630 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3631 syn_clear_cluster(block, i);
3632 ga_clear(&block->b_syn_clusters);
3633 block->b_spell_cluster_id = 0;
3634 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003635
Bram Moolenaar860cae12010-06-05 23:22:07 +02003636 block->b_syn_sync_flags = 0;
3637 block->b_syn_sync_minlines = 0;
3638 block->b_syn_sync_maxlines = 0;
3639 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003640
Bram Moolenaar473de612013-06-08 18:19:48 +02003641 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003642 block->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003643 VIM_CLEAR(block->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003644#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003645 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003646#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003647 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003648
3649 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003650 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003651 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003652
3653 /* Reset the counter for ":syn include" */
3654 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003655}
3656
3657/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003658 * Get rid of ownsyntax for window "wp".
3659 */
3660 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003661reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003662{
3663 if (wp->w_s != &wp->w_buffer->b_s)
3664 {
3665 syntax_clear(wp->w_s);
3666 vim_free(wp->w_s);
3667 wp->w_s = &wp->w_buffer->b_s;
3668 }
3669}
3670
3671/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003672 * Clear syncing info for one buffer.
3673 */
3674 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003675syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003676{
3677 int i;
3678
3679 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003680 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3681 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3682 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003683
Bram Moolenaar860cae12010-06-05 23:22:07 +02003684 curwin->w_s->b_syn_sync_flags = 0;
3685 curwin->w_s->b_syn_sync_minlines = 0;
3686 curwin->w_s->b_syn_sync_maxlines = 0;
3687 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003688
Bram Moolenaar473de612013-06-08 18:19:48 +02003689 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003690 curwin->w_s->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003691 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003692 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003693
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003694 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003695}
3696
3697/*
3698 * Remove one pattern from the buffer's pattern list.
3699 */
3700 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003701syn_remove_pattern(
3702 synblock_T *block,
3703 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003704{
3705 synpat_T *spp;
3706
Bram Moolenaar860cae12010-06-05 23:22:07 +02003707 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003708#ifdef FEAT_FOLDING
3709 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003710 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003711#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003712 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003713 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003714 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3715 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003716}
3717
3718/*
3719 * Clear and free one syntax pattern. When clearing all, must be called from
3720 * last to first!
3721 */
3722 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003723syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003724{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003725 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003726 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003727 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003728 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003729 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003730 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3731 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3732 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003733 }
3734}
3735
3736/*
3737 * Clear and free one syntax cluster.
3738 */
3739 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003740syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003741{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003742 vim_free(SYN_CLSTR(block)[i].scl_name);
3743 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3744 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003745}
3746
3747/*
3748 * Handle ":syntax clear" command.
3749 */
3750 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003751syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003752{
3753 char_u *arg = eap->arg;
3754 char_u *arg_end;
3755 int id;
3756
3757 eap->nextcmd = find_nextcmd(arg);
3758 if (eap->skip)
3759 return;
3760
3761 /*
3762 * We have to disable this within ":syn include @group filename",
3763 * because otherwise @group would get deleted.
3764 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3765 * clear".
3766 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003767 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003768 return;
3769
3770 if (ends_excmd(*arg))
3771 {
3772 /*
3773 * No argument: Clear all syntax items.
3774 */
3775 if (syncing)
3776 syntax_sync_clear();
3777 else
3778 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003779 syntax_clear(curwin->w_s);
3780 if (curwin->w_s == &curwin->w_buffer->b_s)
3781 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003782 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003783 }
3784 }
3785 else
3786 {
3787 /*
3788 * Clear the group IDs that are in the argument.
3789 */
3790 while (!ends_excmd(*arg))
3791 {
3792 arg_end = skiptowhite(arg);
3793 if (*arg == '@')
3794 {
3795 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3796 if (id == 0)
3797 {
3798 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3799 break;
3800 }
3801 else
3802 {
3803 /*
3804 * We can't physically delete a cluster without changing
3805 * the IDs of other clusters, so we do the next best thing
3806 * and make it empty.
3807 */
3808 short scl_id = id - SYNID_CLUSTER;
3809
Bram Moolenaard23a8232018-02-10 18:45:26 +01003810 VIM_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003811 }
3812 }
3813 else
3814 {
3815 id = syn_namen2id(arg, (int)(arg_end - arg));
3816 if (id == 0)
3817 {
3818 EMSG2(_(e_nogroup), arg);
3819 break;
3820 }
3821 else
3822 syn_clear_one(id, syncing);
3823 }
3824 arg = skipwhite(arg_end);
3825 }
3826 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003827 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003828 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003829}
3830
3831/*
3832 * Clear one syntax group for the current buffer.
3833 */
3834 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003835syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003836{
3837 synpat_T *spp;
3838 int idx;
3839
3840 /* Clear keywords only when not ":syn sync clear group-name" */
3841 if (!syncing)
3842 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003843 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3844 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003845 }
3846
3847 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003848 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003849 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003850 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003851 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3852 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003853 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003854 }
3855}
3856
3857/*
3858 * Handle ":syntax on" command.
3859 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003860 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003861syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003862{
3863 syn_cmd_onoff(eap, "syntax");
3864}
3865
3866/*
3867 * Handle ":syntax enable" command.
3868 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003869 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003870syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003871{
3872 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3873 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003874 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003875}
3876
3877/*
3878 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003879 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003880 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003881 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003882syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003883{
3884 eap->nextcmd = check_nextcmd(eap->arg);
3885 if (!eap->skip)
3886 {
3887 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3888 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003889 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003890 }
3891}
3892
3893/*
3894 * Handle ":syntax manual" command.
3895 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003896 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003897syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003898{
3899 syn_cmd_onoff(eap, "manual");
3900}
3901
3902/*
3903 * Handle ":syntax off" command.
3904 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003905 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003906syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003907{
3908 syn_cmd_onoff(eap, "nosyntax");
3909}
3910
3911 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003912syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003913{
3914 char_u buf[100];
3915
3916 eap->nextcmd = check_nextcmd(eap->arg);
3917 if (!eap->skip)
3918 {
3919 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003920 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003921 do_cmdline_cmd(buf);
3922 }
3923}
3924
3925/*
3926 * Handle ":syntax [list]" command: list current syntax words.
3927 */
3928 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003929syn_cmd_list(
3930 exarg_T *eap,
3931 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003932{
3933 char_u *arg = eap->arg;
3934 int id;
3935 char_u *arg_end;
3936
3937 eap->nextcmd = find_nextcmd(arg);
3938 if (eap->skip)
3939 return;
3940
Bram Moolenaar860cae12010-06-05 23:22:07 +02003941 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003942 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003943 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003944 return;
3945 }
3946
3947 if (syncing)
3948 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003949 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003950 {
3951 MSG_PUTS(_("syncing on C-style comments"));
3952 syn_lines_msg();
3953 syn_match_msg();
3954 return;
3955 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003956 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003957 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003958 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003959 MSG_PUTS(_("no syncing"));
3960 else
3961 {
3962 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003963 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003964 MSG_PUTS(_(" lines before top line"));
3965 syn_match_msg();
3966 }
3967 return;
3968 }
3969 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003970 if (curwin->w_s->b_syn_sync_minlines > 0
3971 || curwin->w_s->b_syn_sync_maxlines > 0
3972 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003973 {
3974 MSG_PUTS(_("\nsyncing on items"));
3975 syn_lines_msg();
3976 syn_match_msg();
3977 }
3978 }
3979 else
3980 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3981 if (ends_excmd(*arg))
3982 {
3983 /*
3984 * No argument: List all group IDs and all syntax clusters.
3985 */
3986 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3987 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003988 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003989 syn_list_cluster(id);
3990 }
3991 else
3992 {
3993 /*
3994 * List the group IDs and syntax clusters that are in the argument.
3995 */
3996 while (!ends_excmd(*arg) && !got_int)
3997 {
3998 arg_end = skiptowhite(arg);
3999 if (*arg == '@')
4000 {
4001 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
4002 if (id == 0)
4003 EMSG2(_("E392: No such syntax cluster: %s"), arg);
4004 else
4005 syn_list_cluster(id - SYNID_CLUSTER);
4006 }
4007 else
4008 {
4009 id = syn_namen2id(arg, (int)(arg_end - arg));
4010 if (id == 0)
4011 EMSG2(_(e_nogroup), arg);
4012 else
4013 syn_list_one(id, syncing, TRUE);
4014 }
4015 arg = skipwhite(arg_end);
4016 }
4017 }
4018 eap->nextcmd = check_nextcmd(arg);
4019}
4020
4021 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004022syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004023{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004024 if (curwin->w_s->b_syn_sync_maxlines > 0
4025 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004026 {
4027 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02004028 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004029 {
4030 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004031 msg_outnum(curwin->w_s->b_syn_sync_minlines);
4032 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004033 MSG_PUTS(", ");
4034 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004035 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004036 {
4037 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004038 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004039 }
4040 MSG_PUTS(_(" lines before top line"));
4041 }
4042}
4043
4044 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004045syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004046{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004047 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004048 {
4049 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004050 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004051 MSG_PUTS(_(" line breaks"));
4052 }
4053}
4054
4055static int last_matchgroup;
4056
4057struct name_list
4058{
4059 int flag;
4060 char *name;
4061};
4062
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004063static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004064
4065/*
4066 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4067 */
4068 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004069syn_list_one(
4070 int id,
4071 int syncing, /* when TRUE: list syncing items */
4072 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004073{
4074 int attr;
4075 int idx;
4076 int did_header = FALSE;
4077 synpat_T *spp;
4078 static struct name_list namelist1[] =
4079 {
4080 {HL_DISPLAY, "display"},
4081 {HL_CONTAINED, "contained"},
4082 {HL_ONELINE, "oneline"},
4083 {HL_KEEPEND, "keepend"},
4084 {HL_EXTEND, "extend"},
4085 {HL_EXCLUDENL, "excludenl"},
4086 {HL_TRANSP, "transparent"},
4087 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004088#ifdef FEAT_CONCEAL
4089 {HL_CONCEAL, "conceal"},
4090 {HL_CONCEALENDS, "concealends"},
4091#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004092 {0, NULL}
4093 };
4094 static struct name_list namelist2[] =
4095 {
4096 {HL_SKIPWHITE, "skipwhite"},
4097 {HL_SKIPNL, "skipnl"},
4098 {HL_SKIPEMPTY, "skipempty"},
4099 {0, NULL}
4100 };
4101
Bram Moolenaar8820b482017-03-16 17:23:31 +01004102 attr = HL_ATTR(HLF_D); /* highlight like directories */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004103
4104 /* list the keywords for "id" */
4105 if (!syncing)
4106 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004107 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4108 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004109 did_header, attr);
4110 }
4111
4112 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004113 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004114 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004115 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004116 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4117 continue;
4118
4119 (void)syn_list_header(did_header, 999, id);
4120 did_header = TRUE;
4121 last_matchgroup = 0;
4122 if (spp->sp_type == SPTYPE_MATCH)
4123 {
4124 put_pattern("match", ' ', spp, attr);
4125 msg_putchar(' ');
4126 }
4127 else if (spp->sp_type == SPTYPE_START)
4128 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004129 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4130 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4131 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4132 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4133 while (idx < curwin->w_s->b_syn_patterns.ga_len
4134 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4135 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004136 --idx;
4137 msg_putchar(' ');
4138 }
4139 syn_list_flags(namelist1, spp->sp_flags, attr);
4140
4141 if (spp->sp_cont_list != NULL)
4142 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4143
4144 if (spp->sp_syn.cont_in_list != NULL)
4145 put_id_list((char_u *)"containedin",
4146 spp->sp_syn.cont_in_list, attr);
4147
4148 if (spp->sp_next_list != NULL)
4149 {
4150 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4151 syn_list_flags(namelist2, spp->sp_flags, attr);
4152 }
4153 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4154 {
4155 if (spp->sp_flags & HL_SYNC_HERE)
4156 msg_puts_attr((char_u *)"grouphere", attr);
4157 else
4158 msg_puts_attr((char_u *)"groupthere", attr);
4159 msg_putchar(' ');
4160 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004161 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004162 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4163 else
4164 MSG_PUTS("NONE");
4165 msg_putchar(' ');
4166 }
4167 }
4168
4169 /* list the link, if there is one */
4170 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4171 {
4172 (void)syn_list_header(did_header, 999, id);
4173 msg_puts_attr((char_u *)"links to", attr);
4174 msg_putchar(' ');
4175 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4176 }
4177}
4178
4179 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004180syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004181{
4182 int i;
4183
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004184 for (i = 0; nlist[i].flag != 0; ++i)
4185 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004186 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004187 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004188 msg_putchar(' ');
4189 }
4190}
4191
4192/*
4193 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4194 */
4195 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004196syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004197{
4198 int endcol = 15;
4199
4200 /* slight hack: roughly duplicate the guts of syn_list_header() */
4201 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004202 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004203
4204 if (msg_col >= endcol) /* output at least one space */
4205 endcol = msg_col + 1;
4206 if (Columns <= endcol) /* avoid hang for tiny window */
4207 endcol = Columns - 1;
4208
4209 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004210 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004211 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004212 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004213 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004214 }
4215 else
4216 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01004217 msg_puts_attr((char_u *)"cluster", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004218 msg_puts((char_u *)"=NONE");
4219 }
4220}
4221
4222 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004223put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004224{
4225 short *p;
4226
4227 msg_puts_attr(name, attr);
4228 msg_putchar('=');
4229 for (p = list; *p; ++p)
4230 {
4231 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4232 {
4233 if (p[1])
4234 MSG_PUTS("ALLBUT");
4235 else
4236 MSG_PUTS("ALL");
4237 }
4238 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4239 {
4240 MSG_PUTS("TOP");
4241 }
4242 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4243 {
4244 MSG_PUTS("CONTAINED");
4245 }
4246 else if (*p >= SYNID_CLUSTER)
4247 {
4248 short scl_id = *p - SYNID_CLUSTER;
4249
4250 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004251 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004252 }
4253 else
4254 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4255 if (p[1])
4256 msg_putchar(',');
4257 }
4258 msg_putchar(' ');
4259}
4260
4261 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004262put_pattern(
4263 char *s,
4264 int c,
4265 synpat_T *spp,
4266 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004267{
4268 long n;
4269 int mask;
4270 int first;
4271 static char *sepchars = "/+=-#@\"|'^&";
4272 int i;
4273
4274 /* May have to write "matchgroup=group" */
4275 if (last_matchgroup != spp->sp_syn_match_id)
4276 {
4277 last_matchgroup = spp->sp_syn_match_id;
4278 msg_puts_attr((char_u *)"matchgroup", attr);
4279 msg_putchar('=');
4280 if (last_matchgroup == 0)
4281 msg_outtrans((char_u *)"NONE");
4282 else
4283 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4284 msg_putchar(' ');
4285 }
4286
4287 /* output the name of the pattern and an '=' or ' ' */
4288 msg_puts_attr((char_u *)s, attr);
4289 msg_putchar(c);
4290
4291 /* output the pattern, in between a char that is not in the pattern */
4292 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4293 if (sepchars[++i] == NUL)
4294 {
4295 i = 0; /* no good char found, just use the first one */
4296 break;
4297 }
4298 msg_putchar(sepchars[i]);
4299 msg_outtrans(spp->sp_pattern);
4300 msg_putchar(sepchars[i]);
4301
4302 /* output any pattern options */
4303 first = TRUE;
4304 for (i = 0; i < SPO_COUNT; ++i)
4305 {
4306 mask = (1 << i);
4307 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4308 {
4309 if (!first)
4310 msg_putchar(','); /* separate with commas */
4311 msg_puts((char_u *)spo_name_tab[i]);
4312 n = spp->sp_offsets[i];
4313 if (i != SPO_LC_OFF)
4314 {
4315 if (spp->sp_off_flags & mask)
4316 msg_putchar('s');
4317 else
4318 msg_putchar('e');
4319 if (n > 0)
4320 msg_putchar('+');
4321 }
4322 if (n || i == SPO_LC_OFF)
4323 msg_outnum(n);
4324 first = FALSE;
4325 }
4326 }
4327 msg_putchar(' ');
4328}
4329
4330/*
4331 * List or clear the keywords for one syntax group.
4332 * Return TRUE if the header has been printed.
4333 */
4334 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004335syn_list_keywords(
4336 int id,
4337 hashtab_T *ht,
4338 int did_header, /* header has already been printed */
4339 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004340{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004341 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004342 hashitem_T *hi;
4343 keyentry_T *kp;
4344 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004345 int prev_contained = 0;
4346 short *prev_next_list = NULL;
4347 short *prev_cont_in_list = NULL;
4348 int prev_skipnl = 0;
4349 int prev_skipwhite = 0;
4350 int prev_skipempty = 0;
4351
Bram Moolenaar071d4272004-06-13 20:20:40 +00004352 /*
4353 * Unfortunately, this list of keywords is not sorted on alphabet but on
4354 * hash value...
4355 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004356 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004357 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004358 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004359 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004360 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004361 --todo;
4362 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004363 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004364 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004365 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004366 if (prev_contained != (kp->flags & HL_CONTAINED)
4367 || prev_skipnl != (kp->flags & HL_SKIPNL)
4368 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4369 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4370 || prev_cont_in_list != kp->k_syn.cont_in_list
4371 || prev_next_list != kp->next_list)
4372 outlen = 9999;
4373 else
4374 outlen = (int)STRLEN(kp->keyword);
4375 /* output "contained" and "nextgroup" on each line */
4376 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004377 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004378 prev_contained = 0;
4379 prev_next_list = NULL;
4380 prev_cont_in_list = NULL;
4381 prev_skipnl = 0;
4382 prev_skipwhite = 0;
4383 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004384 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004385 did_header = TRUE;
4386 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004387 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004388 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004389 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004390 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004391 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004392 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004393 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004394 put_id_list((char_u *)"containedin",
4395 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004396 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004397 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004398 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004399 if (kp->next_list != prev_next_list)
4400 {
4401 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4402 msg_putchar(' ');
4403 prev_next_list = kp->next_list;
4404 if (kp->flags & HL_SKIPNL)
4405 {
4406 msg_puts_attr((char_u *)"skipnl", attr);
4407 msg_putchar(' ');
4408 prev_skipnl = (kp->flags & HL_SKIPNL);
4409 }
4410 if (kp->flags & HL_SKIPWHITE)
4411 {
4412 msg_puts_attr((char_u *)"skipwhite", attr);
4413 msg_putchar(' ');
4414 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4415 }
4416 if (kp->flags & HL_SKIPEMPTY)
4417 {
4418 msg_puts_attr((char_u *)"skipempty", attr);
4419 msg_putchar(' ');
4420 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4421 }
4422 }
4423 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004424 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004425 }
4426 }
4427 }
4428
4429 return did_header;
4430}
4431
4432 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004433syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004434{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004435 hashitem_T *hi;
4436 keyentry_T *kp;
4437 keyentry_T *kp_prev;
4438 keyentry_T *kp_next;
4439 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004440
Bram Moolenaardad6b692005-01-25 22:14:34 +00004441 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004442 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004443 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004444 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004445 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004446 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004447 --todo;
4448 kp_prev = NULL;
4449 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004450 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004451 if (kp->k_syn.id == id)
4452 {
4453 kp_next = kp->ke_next;
4454 if (kp_prev == NULL)
4455 {
4456 if (kp_next == NULL)
4457 hash_remove(ht, hi);
4458 else
4459 hi->hi_key = KE2HIKEY(kp_next);
4460 }
4461 else
4462 kp_prev->ke_next = kp_next;
4463 vim_free(kp->next_list);
4464 vim_free(kp->k_syn.cont_in_list);
4465 vim_free(kp);
4466 kp = kp_next;
4467 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004468 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004469 {
4470 kp_prev = kp;
4471 kp = kp->ke_next;
4472 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004473 }
4474 }
4475 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004476 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004477}
4478
4479/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004480 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004481 */
4482 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004483clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004484{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004485 hashitem_T *hi;
4486 int todo;
4487 keyentry_T *kp;
4488 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004489
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004490 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004491 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004492 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004493 if (!HASHITEM_EMPTY(hi))
4494 {
4495 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004496 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004497 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004498 kp_next = kp->ke_next;
4499 vim_free(kp->next_list);
4500 vim_free(kp->k_syn.cont_in_list);
4501 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004502 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004503 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004504 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004505 hash_clear(ht);
4506 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004507}
4508
4509/*
4510 * Add a keyword to the list of keywords.
4511 */
4512 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004513add_keyword(
4514 char_u *name, /* name of keyword */
4515 int id, /* group ID for this keyword */
4516 int flags, /* flags for this keyword */
4517 short *cont_in_list, /* containedin for this keyword */
4518 short *next_list, /* nextgroup for this keyword */
4519 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004520{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004521 keyentry_T *kp;
4522 hashtab_T *ht;
4523 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004524 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004525 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004526 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004527
Bram Moolenaar860cae12010-06-05 23:22:07 +02004528 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004529 name_ic = str_foldcase(name, (int)STRLEN(name),
4530 name_folded, MAXKEYWLEN + 1);
4531 else
4532 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004533 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4534 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004535 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004536 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004537 kp->k_syn.id = id;
4538 kp->k_syn.inc_tag = current_syn_inc_tag;
4539 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004540 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004541 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004542 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004543 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004544 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004545
Bram Moolenaar860cae12010-06-05 23:22:07 +02004546 if (curwin->w_s->b_syn_ic)
4547 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004548 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004549 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004550
Bram Moolenaardad6b692005-01-25 22:14:34 +00004551 hash = hash_hash(kp->keyword);
4552 hi = hash_lookup(ht, kp->keyword, hash);
4553 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004554 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004555 /* new keyword, add to hashtable */
4556 kp->ke_next = NULL;
4557 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004558 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004559 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004560 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004561 /* keyword already exists, prepend to list */
4562 kp->ke_next = HI2KE(hi);
4563 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004564 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004565}
4566
4567/*
4568 * Get the start and end of the group name argument.
4569 * Return a pointer to the first argument.
4570 * Return NULL if the end of the command was found instead of further args.
4571 */
4572 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004573get_group_name(
4574 char_u *arg, /* start of the argument */
4575 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004576{
4577 char_u *rest;
4578
4579 *name_end = skiptowhite(arg);
4580 rest = skipwhite(*name_end);
4581
4582 /*
4583 * Check if there are enough arguments. The first argument may be a
4584 * pattern, where '|' is allowed, so only check for NUL.
4585 */
4586 if (ends_excmd(*arg) || *rest == NUL)
4587 return NULL;
4588 return rest;
4589}
4590
4591/*
4592 * Check for syntax command option arguments.
4593 * This can be called at any place in the list of arguments, and just picks
4594 * out the arguments that are known. Can be called several times in a row to
4595 * collect all options in between other arguments.
4596 * Return a pointer to the next argument (which isn't an option).
4597 * Return NULL for any error;
4598 */
4599 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004600get_syn_options(
4601 char_u *arg, /* next argument to be checked */
4602 syn_opt_arg_T *opt, /* various things */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004603 int *conceal_char UNUSED,
4604 int skip) /* TRUE if skipping over command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004605{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004606 char_u *gname_start, *gname;
4607 int syn_id;
4608 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004609 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004610 int i;
4611 int fidx;
4612 static struct flag
4613 {
4614 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004615 int argtype;
4616 int flags;
4617 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4618 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4619 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4620 {"eExXtTeEnNdD", 0, HL_EXTEND},
4621 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4622 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4623 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4624 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4625 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4626 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4627 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4628 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4629 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004630 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4631 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4632 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004633 {"cCoOnNtTaAiInNsS", 1, 0},
4634 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4635 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004636 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004637 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004638
4639 if (arg == NULL) /* already detected error */
4640 return NULL;
4641
Bram Moolenaar860cae12010-06-05 23:22:07 +02004642#ifdef FEAT_CONCEAL
4643 if (curwin->w_s->b_syn_conceal)
4644 opt->flags |= HL_CONCEAL;
4645#endif
4646
Bram Moolenaar071d4272004-06-13 20:20:40 +00004647 for (;;)
4648 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004649 /*
4650 * This is used very often when a large number of keywords is defined.
4651 * Need to skip quickly when no option name is found.
4652 * Also avoid tolower(), it's slow.
4653 */
4654 if (strchr(first_letters, *arg) == NULL)
4655 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004656
4657 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4658 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004659 p = flagtab[fidx].name;
4660 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4661 if (arg[len] != p[i] && arg[len] != p[i + 1])
4662 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004663 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004664 || (flagtab[fidx].argtype > 0
4665 ? arg[len] == '='
4666 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004667 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004668 if (opt->keyword
4669 && (flagtab[fidx].flags == HL_DISPLAY
4670 || flagtab[fidx].flags == HL_FOLD
4671 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004672 /* treat "display", "fold" and "extend" as a keyword */
4673 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004674 break;
4675 }
4676 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004677 if (fidx < 0) /* no match found */
4678 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004679
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004680 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004681 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004682 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004683 {
4684 EMSG(_("E395: contains argument not accepted here"));
4685 return NULL;
4686 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004687 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004688 return NULL;
4689 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004690 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004691 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004692 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004693 return NULL;
4694 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004695 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004696 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004697 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004698 return NULL;
4699 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004700 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4701 {
4702#ifdef FEAT_MBYTE
4703 /* cchar=? */
4704 if (has_mbyte)
4705 {
4706# ifdef FEAT_CONCEAL
4707 *conceal_char = mb_ptr2char(arg + 6);
4708# endif
4709 arg += mb_ptr2len(arg + 6) - 1;
4710 }
4711 else
4712#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004713 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004714#ifdef FEAT_CONCEAL
4715 *conceal_char = arg[6];
4716#else
4717 ;
4718#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004719 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004720#ifdef FEAT_CONCEAL
4721 if (!vim_isprintc_strict(*conceal_char))
4722 {
4723 EMSG(_("E844: invalid cchar value"));
4724 return NULL;
4725 }
4726#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004727 arg = skipwhite(arg + 7);
4728 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004729 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004730 {
4731 opt->flags |= flagtab[fidx].flags;
4732 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004733
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004734 if (flagtab[fidx].flags == HL_SYNC_HERE
4735 || flagtab[fidx].flags == HL_SYNC_THERE)
4736 {
4737 if (opt->sync_idx == NULL)
4738 {
4739 EMSG(_("E393: group[t]here not accepted here"));
4740 return NULL;
4741 }
4742 gname_start = arg;
4743 arg = skiptowhite(arg);
4744 if (gname_start == arg)
4745 return NULL;
4746 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4747 if (gname == NULL)
4748 return NULL;
4749 if (STRCMP(gname, "NONE") == 0)
4750 *opt->sync_idx = NONE_IDX;
4751 else
4752 {
4753 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004754 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4755 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4756 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004757 {
4758 *opt->sync_idx = i;
4759 break;
4760 }
4761 if (i < 0)
4762 {
4763 EMSG2(_("E394: Didn't find region item for %s"), gname);
4764 vim_free(gname);
4765 return NULL;
4766 }
4767 }
4768
4769 vim_free(gname);
4770 arg = skipwhite(arg);
4771 }
4772#ifdef FEAT_FOLDING
4773 else if (flagtab[fidx].flags == HL_FOLD
4774 && foldmethodIsSyntax(curwin))
4775 /* Need to update folds later. */
4776 foldUpdateAll(curwin);
4777#endif
4778 }
4779 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004780
4781 return arg;
4782}
4783
4784/*
4785 * Adjustments to syntax item when declared in a ":syn include"'d file.
4786 * Set the contained flag, and if the item is not already contained, add it
4787 * to the specified top-level group, if any.
4788 */
4789 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004790syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004791{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004792 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004793 return;
4794 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004795 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004796 {
4797 /* We have to alloc this, because syn_combine_list() will free it. */
4798 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004799 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004800
4801 if (grp_list != NULL)
4802 {
4803 grp_list[0] = id;
4804 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004805 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004806 CLUSTER_ADD);
4807 }
4808 }
4809}
4810
4811/*
4812 * Handle ":syntax include [@{group-name}] filename" command.
4813 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004814 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004815syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004816{
4817 char_u *arg = eap->arg;
4818 int sgl_id = 1;
4819 char_u *group_name_end;
4820 char_u *rest;
4821 char_u *errormsg = NULL;
4822 int prev_toplvl_grp;
4823 int prev_syn_inc_tag;
4824 int source = FALSE;
4825
4826 eap->nextcmd = find_nextcmd(arg);
4827 if (eap->skip)
4828 return;
4829
4830 if (arg[0] == '@')
4831 {
4832 ++arg;
4833 rest = get_group_name(arg, &group_name_end);
4834 if (rest == NULL)
4835 {
4836 EMSG((char_u *)_("E397: Filename required"));
4837 return;
4838 }
4839 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004840 if (sgl_id == 0)
4841 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004842 /* separate_nextcmd() and expand_filename() depend on this */
4843 eap->arg = rest;
4844 }
4845
4846 /*
4847 * Everything that's left, up to the next command, should be the
4848 * filename to include.
4849 */
4850 eap->argt |= (XFILE | NOSPC);
4851 separate_nextcmd(eap);
4852 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4853 {
4854 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4855 * file. Need to expand the file name first. In other cases
4856 * ":runtime!" is used. */
4857 source = TRUE;
4858 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4859 {
4860 if (errormsg != NULL)
4861 EMSG(errormsg);
4862 return;
4863 }
4864 }
4865
4866 /*
4867 * Save and restore the existing top-level grouplist id and ":syn
4868 * include" tag around the actual inclusion.
4869 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004870 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4871 {
4872 EMSG((char_u *)_("E847: Too many syntax includes"));
4873 return;
4874 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004875 prev_syn_inc_tag = current_syn_inc_tag;
4876 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004877 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4878 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004879 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004880 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004881 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004882 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004883 current_syn_inc_tag = prev_syn_inc_tag;
4884}
4885
4886/*
4887 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4888 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004889 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004890syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004891{
4892 char_u *arg = eap->arg;
4893 char_u *group_name_end;
4894 int syn_id;
4895 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004896 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004897 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004898 char_u *kw;
4899 syn_opt_arg_T syn_opt_arg;
4900 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004901 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004902
4903 rest = get_group_name(arg, &group_name_end);
4904
4905 if (rest != NULL)
4906 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004907 if (eap->skip)
4908 syn_id = -1;
4909 else
4910 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004911 if (syn_id != 0)
4912 /* allocate a buffer, for removing backslashes in the keyword */
4913 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004914 if (keyword_copy != NULL)
4915 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004916 syn_opt_arg.flags = 0;
4917 syn_opt_arg.keyword = TRUE;
4918 syn_opt_arg.sync_idx = NULL;
4919 syn_opt_arg.has_cont_list = FALSE;
4920 syn_opt_arg.cont_in_list = NULL;
4921 syn_opt_arg.next_list = NULL;
4922
Bram Moolenaar071d4272004-06-13 20:20:40 +00004923 /*
4924 * The options given apply to ALL keywords, so all options must be
4925 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004926 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004927 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004928 cnt = 0;
4929 p = keyword_copy;
4930 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004931 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004932 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4933 eap->skip);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004934 if (rest == NULL || ends_excmd(*rest))
4935 break;
4936 /* Copy the keyword, removing backslashes, and add a NUL. */
Bram Moolenaar1c465442017-03-12 20:10:05 +01004937 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004938 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004939 if (*rest == '\\' && rest[1] != NUL)
4940 ++rest;
4941 *p++ = *rest++;
4942 }
4943 *p++ = NUL;
4944 ++cnt;
4945 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004946
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004947 if (!eap->skip)
4948 {
4949 /* Adjust flags for use of ":syn include". */
4950 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4951
4952 /*
4953 * 2: Add an entry for each keyword.
4954 */
4955 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4956 {
4957 for (p = vim_strchr(kw, '['); ; )
4958 {
4959 if (p != NULL)
4960 *p = NUL;
4961 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004962 syn_opt_arg.cont_in_list,
4963 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004964 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004965 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004966 if (p[1] == NUL)
4967 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004968 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004969 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004970 }
4971 if (p[1] == ']')
4972 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004973 if (p[2] != NUL)
4974 {
4975 EMSG3(_("E890: trailing char after ']': %s]%s"),
4976 kw, &p[2]);
4977 goto error;
4978 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004979 kw = p + 1; /* skip over the "]" */
4980 break;
4981 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004982#ifdef FEAT_MBYTE
4983 if (has_mbyte)
4984 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004985 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004986
4987 mch_memmove(p, p + 1, l);
4988 p += l;
4989 }
4990 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004991#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004992 {
4993 p[0] = p[1];
4994 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004995 }
4996 }
4997 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004998 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004999error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005000 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00005001 vim_free(syn_opt_arg.cont_in_list);
5002 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005003 }
5004 }
5005
5006 if (rest != NULL)
5007 eap->nextcmd = check_nextcmd(rest);
5008 else
5009 EMSG2(_(e_invarg2), arg);
5010
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005011 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005012 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005013}
5014
5015/*
5016 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
5017 *
5018 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
5019 */
5020 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005021syn_cmd_match(
5022 exarg_T *eap,
5023 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005024{
5025 char_u *arg = eap->arg;
5026 char_u *group_name_end;
5027 char_u *rest;
5028 synpat_T item; /* the item found in the line */
5029 int syn_id;
5030 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005031 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005032 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005033 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005034
5035 /* Isolate the group name, check for validity */
5036 rest = get_group_name(arg, &group_name_end);
5037
5038 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005039 syn_opt_arg.flags = 0;
5040 syn_opt_arg.keyword = FALSE;
5041 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
5042 syn_opt_arg.has_cont_list = TRUE;
5043 syn_opt_arg.cont_list = NULL;
5044 syn_opt_arg.cont_in_list = NULL;
5045 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005046 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005047
5048 /* get the pattern. */
5049 init_syn_patterns();
5050 vim_memset(&item, 0, sizeof(item));
5051 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005052 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
5053 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005054
5055 /* Get options after the pattern */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005056 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005057
5058 if (rest != NULL) /* all arguments are valid */
5059 {
5060 /*
5061 * Check for trailing command and illegal trailing arguments.
5062 */
5063 eap->nextcmd = check_nextcmd(rest);
5064 if (!ends_excmd(*rest) || eap->skip)
5065 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005066 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005067 && (syn_id = syn_check_group(arg,
5068 (int)(group_name_end - arg))) != 0)
5069 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005070 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005071 /*
5072 * Store the pattern in the syn_items list
5073 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005074 idx = curwin->w_s->b_syn_patterns.ga_len;
5075 SYN_ITEMS(curwin->w_s)[idx] = item;
5076 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5077 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5078 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5079 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5080 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5081 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5082 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5083 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005084 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005085#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005086 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005087#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005088 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005089 curwin->w_s->b_syn_containedin = TRUE;
5090 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5091 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005092
5093 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005094 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005095 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005096#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005097 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005098 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005099#endif
5100
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005101 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005102 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005103 return; /* don't free the progs and patterns now */
5104 }
5105 }
5106
5107 /*
5108 * Something failed, free the allocated memory.
5109 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005110 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005111 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005112 vim_free(syn_opt_arg.cont_list);
5113 vim_free(syn_opt_arg.cont_in_list);
5114 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005115
5116 if (rest == NULL)
5117 EMSG2(_(e_invarg2), arg);
5118}
5119
5120/*
5121 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5122 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5123 */
5124 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005125syn_cmd_region(
5126 exarg_T *eap,
5127 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005128{
5129 char_u *arg = eap->arg;
5130 char_u *group_name_end;
5131 char_u *rest; /* next arg, NULL on error */
5132 char_u *key_end;
5133 char_u *key = NULL;
5134 char_u *p;
5135 int item;
5136#define ITEM_START 0
5137#define ITEM_SKIP 1
5138#define ITEM_END 2
5139#define ITEM_MATCHGROUP 3
5140 struct pat_ptr
5141 {
5142 synpat_T *pp_synp; /* pointer to syn_pattern */
5143 int pp_matchgroup_id; /* matchgroup ID */
5144 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5145 } *(pat_ptrs[3]);
5146 /* patterns found in the line */
5147 struct pat_ptr *ppp;
5148 struct pat_ptr *ppp_next;
5149 int pat_count = 0; /* nr of syn_patterns found */
5150 int syn_id;
5151 int matchgroup_id = 0;
5152 int not_enough = FALSE; /* not enough arguments */
5153 int illegal = FALSE; /* illegal arguments */
5154 int success = FALSE;
5155 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005156 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005157 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005158
5159 /* Isolate the group name, check for validity */
5160 rest = get_group_name(arg, &group_name_end);
5161
5162 pat_ptrs[0] = NULL;
5163 pat_ptrs[1] = NULL;
5164 pat_ptrs[2] = NULL;
5165
5166 init_syn_patterns();
5167
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005168 syn_opt_arg.flags = 0;
5169 syn_opt_arg.keyword = FALSE;
5170 syn_opt_arg.sync_idx = NULL;
5171 syn_opt_arg.has_cont_list = TRUE;
5172 syn_opt_arg.cont_list = NULL;
5173 syn_opt_arg.cont_in_list = NULL;
5174 syn_opt_arg.next_list = NULL;
5175
Bram Moolenaar071d4272004-06-13 20:20:40 +00005176 /*
5177 * get the options, patterns and matchgroup.
5178 */
5179 while (rest != NULL && !ends_excmd(*rest))
5180 {
5181 /* Check for option arguments */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005182 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005183 if (rest == NULL || ends_excmd(*rest))
5184 break;
5185
5186 /* must be a pattern or matchgroup then */
5187 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005188 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005189 ++key_end;
5190 vim_free(key);
5191 key = vim_strnsave_up(rest, (int)(key_end - rest));
5192 if (key == NULL) /* out of memory */
5193 {
5194 rest = NULL;
5195 break;
5196 }
5197 if (STRCMP(key, "MATCHGROUP") == 0)
5198 item = ITEM_MATCHGROUP;
5199 else if (STRCMP(key, "START") == 0)
5200 item = ITEM_START;
5201 else if (STRCMP(key, "END") == 0)
5202 item = ITEM_END;
5203 else if (STRCMP(key, "SKIP") == 0)
5204 {
5205 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5206 {
5207 illegal = TRUE;
5208 break;
5209 }
5210 item = ITEM_SKIP;
5211 }
5212 else
5213 break;
5214 rest = skipwhite(key_end);
5215 if (*rest != '=')
5216 {
5217 rest = NULL;
5218 EMSG2(_("E398: Missing '=': %s"), arg);
5219 break;
5220 }
5221 rest = skipwhite(rest + 1);
5222 if (*rest == NUL)
5223 {
5224 not_enough = TRUE;
5225 break;
5226 }
5227
5228 if (item == ITEM_MATCHGROUP)
5229 {
5230 p = skiptowhite(rest);
5231 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5232 matchgroup_id = 0;
5233 else
5234 {
5235 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5236 if (matchgroup_id == 0)
5237 {
5238 illegal = TRUE;
5239 break;
5240 }
5241 }
5242 rest = skipwhite(p);
5243 }
5244 else
5245 {
5246 /*
5247 * Allocate room for a syn_pattern, and link it in the list of
5248 * syn_patterns for this item, at the start (because the list is
5249 * used from end to start).
5250 */
5251 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5252 if (ppp == NULL)
5253 {
5254 rest = NULL;
5255 break;
5256 }
5257 ppp->pp_next = pat_ptrs[item];
5258 pat_ptrs[item] = ppp;
5259 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5260 if (ppp->pp_synp == NULL)
5261 {
5262 rest = NULL;
5263 break;
5264 }
5265
5266 /*
5267 * Get the syntax pattern and the following offset(s).
5268 */
5269 /* Enable the appropriate \z specials. */
5270 if (item == ITEM_START)
5271 reg_do_extmatch = REX_SET;
5272 else if (item == ITEM_SKIP || item == ITEM_END)
5273 reg_do_extmatch = REX_USE;
5274 rest = get_syn_pattern(rest, ppp->pp_synp);
5275 reg_do_extmatch = 0;
5276 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005277 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005278 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5279 ppp->pp_matchgroup_id = matchgroup_id;
5280 ++pat_count;
5281 }
5282 }
5283 vim_free(key);
5284 if (illegal || not_enough)
5285 rest = NULL;
5286
5287 /*
5288 * Must have a "start" and "end" pattern.
5289 */
5290 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5291 pat_ptrs[ITEM_END] == NULL))
5292 {
5293 not_enough = TRUE;
5294 rest = NULL;
5295 }
5296
5297 if (rest != NULL)
5298 {
5299 /*
5300 * Check for trailing garbage or command.
5301 * If OK, add the item.
5302 */
5303 eap->nextcmd = check_nextcmd(rest);
5304 if (!ends_excmd(*rest) || eap->skip)
5305 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005306 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005307 && (syn_id = syn_check_group(arg,
5308 (int)(group_name_end - arg))) != 0)
5309 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005310 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005311 /*
5312 * Store the start/skip/end in the syn_items list
5313 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005314 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005315 for (item = ITEM_START; item <= ITEM_END; ++item)
5316 {
5317 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5318 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005319 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5320 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5321 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005322 (item == ITEM_START) ? SPTYPE_START :
5323 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005324 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5325 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005326 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5327 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005328 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005329 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005330#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005331 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005332#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005333 if (item == ITEM_START)
5334 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005335 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005336 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005337 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005338 syn_opt_arg.cont_in_list;
5339 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005340 curwin->w_s->b_syn_containedin = TRUE;
5341 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005342 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005343 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005344 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005345 ++idx;
5346#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005347 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005348 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005349#endif
5350 }
5351 }
5352
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005353 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005354 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005355 success = TRUE; /* don't free the progs and patterns now */
5356 }
5357 }
5358
5359 /*
5360 * Free the allocated memory.
5361 */
5362 for (item = ITEM_START; item <= ITEM_END; ++item)
5363 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5364 {
5365 if (!success)
5366 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005367 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005368 vim_free(ppp->pp_synp->sp_pattern);
5369 }
5370 vim_free(ppp->pp_synp);
5371 ppp_next = ppp->pp_next;
5372 vim_free(ppp);
5373 }
5374
5375 if (!success)
5376 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005377 vim_free(syn_opt_arg.cont_list);
5378 vim_free(syn_opt_arg.cont_in_list);
5379 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005380 if (not_enough)
5381 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5382 else if (illegal || rest == NULL)
5383 EMSG2(_(e_invarg2), arg);
5384 }
5385}
5386
5387/*
5388 * A simple syntax group ID comparison function suitable for use in qsort()
5389 */
5390 static int
5391#ifdef __BORLANDC__
5392_RTLENTRYF
5393#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005394syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005395{
5396 const short *s1 = v1;
5397 const short *s2 = v2;
5398
5399 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5400}
5401
5402/*
5403 * Combines lists of syntax clusters.
5404 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5405 */
5406 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005407syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005408{
5409 int count1 = 0;
5410 int count2 = 0;
5411 short *g1;
5412 short *g2;
5413 short *clstr = NULL;
5414 int count;
5415 int round;
5416
5417 /*
5418 * Handle degenerate cases.
5419 */
5420 if (*clstr2 == NULL)
5421 return;
5422 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5423 {
5424 if (list_op == CLUSTER_REPLACE)
5425 vim_free(*clstr1);
5426 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5427 *clstr1 = *clstr2;
5428 else
5429 vim_free(*clstr2);
5430 return;
5431 }
5432
5433 for (g1 = *clstr1; *g1; g1++)
5434 ++count1;
5435 for (g2 = *clstr2; *g2; g2++)
5436 ++count2;
5437
5438 /*
5439 * For speed purposes, sort both lists.
5440 */
5441 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5442 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5443
5444 /*
5445 * We proceed in two passes; in round 1, we count the elements to place
5446 * in the new list, and in round 2, we allocate and populate the new
5447 * list. For speed, we use a mergesort-like method, adding the smaller
5448 * of the current elements in each list to the new list.
5449 */
5450 for (round = 1; round <= 2; round++)
5451 {
5452 g1 = *clstr1;
5453 g2 = *clstr2;
5454 count = 0;
5455
5456 /*
5457 * First, loop through the lists until one of them is empty.
5458 */
5459 while (*g1 && *g2)
5460 {
5461 /*
5462 * We always want to add from the first list.
5463 */
5464 if (*g1 < *g2)
5465 {
5466 if (round == 2)
5467 clstr[count] = *g1;
5468 count++;
5469 g1++;
5470 continue;
5471 }
5472 /*
5473 * We only want to add from the second list if we're adding the
5474 * lists.
5475 */
5476 if (list_op == CLUSTER_ADD)
5477 {
5478 if (round == 2)
5479 clstr[count] = *g2;
5480 count++;
5481 }
5482 if (*g1 == *g2)
5483 g1++;
5484 g2++;
5485 }
5486
5487 /*
5488 * Now add the leftovers from whichever list didn't get finished
5489 * first. As before, we only want to add from the second list if
5490 * we're adding the lists.
5491 */
5492 for (; *g1; g1++, count++)
5493 if (round == 2)
5494 clstr[count] = *g1;
5495 if (list_op == CLUSTER_ADD)
5496 for (; *g2; g2++, count++)
5497 if (round == 2)
5498 clstr[count] = *g2;
5499
5500 if (round == 1)
5501 {
5502 /*
5503 * If the group ended up empty, we don't need to allocate any
5504 * space for it.
5505 */
5506 if (count == 0)
5507 {
5508 clstr = NULL;
5509 break;
5510 }
5511 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5512 if (clstr == NULL)
5513 break;
5514 clstr[count] = 0;
5515 }
5516 }
5517
5518 /*
5519 * Finally, put the new list in place.
5520 */
5521 vim_free(*clstr1);
5522 vim_free(*clstr2);
5523 *clstr1 = clstr;
5524}
5525
5526/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005527 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005528 * If it is not found, 0 is returned.
5529 */
5530 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005531syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005532{
5533 int i;
5534 char_u *name_u;
5535
5536 /* Avoid using stricmp() too much, it's slow on some systems */
5537 name_u = vim_strsave_up(name);
5538 if (name_u == NULL)
5539 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005540 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5541 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5542 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005543 break;
5544 vim_free(name_u);
5545 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5546}
5547
5548/*
5549 * Like syn_scl_name2id(), but take a pointer + length argument.
5550 */
5551 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005552syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005553{
5554 char_u *name;
5555 int id = 0;
5556
5557 name = vim_strnsave(linep, len);
5558 if (name != NULL)
5559 {
5560 id = syn_scl_name2id(name);
5561 vim_free(name);
5562 }
5563 return id;
5564}
5565
5566/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005567 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005568 * The argument is a pointer to the name and the length of the name.
5569 * If it doesn't exist yet, a new entry is created.
5570 * Return 0 for failure.
5571 */
5572 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005573syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005574{
5575 int id;
5576 char_u *name;
5577
5578 name = vim_strnsave(pp, len);
5579 if (name == NULL)
5580 return 0;
5581
5582 id = syn_scl_name2id(name);
5583 if (id == 0) /* doesn't exist yet */
5584 id = syn_add_cluster(name);
5585 else
5586 vim_free(name);
5587 return id;
5588}
5589
5590/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005591 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005592 * "name" must be an allocated string, it will be consumed.
5593 * Return 0 for failure.
5594 */
5595 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005596syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005597{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005598 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005599
5600 /*
5601 * First call for this growarray: init growing array.
5602 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005603 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005604 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005605 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5606 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005607 }
5608
Bram Moolenaar42431a72011-04-01 14:44:59 +02005609 len = curwin->w_s->b_syn_clusters.ga_len;
5610 if (len >= MAX_CLUSTER_ID)
5611 {
5612 EMSG((char_u *)_("E848: Too many syntax clusters"));
5613 vim_free(name);
5614 return 0;
5615 }
5616
Bram Moolenaar071d4272004-06-13 20:20:40 +00005617 /*
5618 * Make room for at least one other cluster entry.
5619 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005620 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005621 {
5622 vim_free(name);
5623 return 0;
5624 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005625
Bram Moolenaar860cae12010-06-05 23:22:07 +02005626 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5627 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5628 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5629 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5630 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005631
Bram Moolenaar217ad922005-03-20 22:37:15 +00005632 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005633 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005634 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005635 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005636
Bram Moolenaar071d4272004-06-13 20:20:40 +00005637 return len + SYNID_CLUSTER;
5638}
5639
5640/*
5641 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5642 * [add={groupname},..] [remove={groupname},..]".
5643 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005644 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005645syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005646{
5647 char_u *arg = eap->arg;
5648 char_u *group_name_end;
5649 char_u *rest;
5650 int scl_id;
5651 short *clstr_list;
5652 int got_clstr = FALSE;
5653 int opt_len;
5654 int list_op;
5655
5656 eap->nextcmd = find_nextcmd(arg);
5657 if (eap->skip)
5658 return;
5659
5660 rest = get_group_name(arg, &group_name_end);
5661
5662 if (rest != NULL)
5663 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005664 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5665 if (scl_id == 0)
5666 return;
5667 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005668
5669 for (;;)
5670 {
5671 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005672 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005673 {
5674 opt_len = 3;
5675 list_op = CLUSTER_ADD;
5676 }
5677 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005678 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005679 {
5680 opt_len = 6;
5681 list_op = CLUSTER_SUBTRACT;
5682 }
5683 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005684 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005685 {
5686 opt_len = 8;
5687 list_op = CLUSTER_REPLACE;
5688 }
5689 else
5690 break;
5691
5692 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005693 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005694 {
5695 EMSG2(_(e_invarg2), rest);
5696 break;
5697 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005698 if (scl_id >= 0)
5699 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005700 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005701 else
5702 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005703 got_clstr = TRUE;
5704 }
5705
5706 if (got_clstr)
5707 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005708 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005709 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005710 }
5711 }
5712
5713 if (!got_clstr)
5714 EMSG(_("E400: No cluster specified"));
5715 if (rest == NULL || !ends_excmd(*rest))
5716 EMSG2(_(e_invarg2), arg);
5717}
5718
5719/*
5720 * On first call for current buffer: Init growing array.
5721 */
5722 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005723init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005724{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005725 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5726 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005727}
5728
5729/*
5730 * Get one pattern for a ":syntax match" or ":syntax region" command.
5731 * Stores the pattern and program in a synpat_T.
5732 * Returns a pointer to the next argument, or NULL in case of an error.
5733 */
5734 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005735get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005736{
5737 char_u *end;
5738 int *p;
5739 int idx;
5740 char_u *cpo_save;
5741
5742 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005743 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005744 return NULL;
5745
5746 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5747 if (*end != *arg) /* end delimiter not found */
5748 {
5749 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5750 return NULL;
5751 }
5752 /* store the pattern and compiled regexp program */
5753 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5754 return NULL;
5755
5756 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5757 cpo_save = p_cpo;
5758 p_cpo = (char_u *)"";
5759 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5760 p_cpo = cpo_save;
5761
5762 if (ci->sp_prog == NULL)
5763 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005764 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005765#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005766 syn_clear_time(&ci->sp_time);
5767#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005768
5769 /*
5770 * Check for a match, highlight or region offset.
5771 */
5772 ++end;
5773 do
5774 {
5775 for (idx = SPO_COUNT; --idx >= 0; )
5776 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5777 break;
5778 if (idx >= 0)
5779 {
5780 p = &(ci->sp_offsets[idx]);
5781 if (idx != SPO_LC_OFF)
5782 switch (end[3])
5783 {
5784 case 's': break;
5785 case 'b': break;
5786 case 'e': idx += SPO_COUNT; break;
5787 default: idx = -1; break;
5788 }
5789 if (idx >= 0)
5790 {
5791 ci->sp_off_flags |= (1 << idx);
5792 if (idx == SPO_LC_OFF) /* lc=99 */
5793 {
5794 end += 3;
5795 *p = getdigits(&end);
5796
5797 /* "lc=" offset automatically sets "ms=" offset */
5798 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5799 {
5800 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5801 ci->sp_offsets[SPO_MS_OFF] = *p;
5802 }
5803 }
5804 else /* yy=x+99 */
5805 {
5806 end += 4;
5807 if (*end == '+')
5808 {
5809 ++end;
5810 *p = getdigits(&end); /* positive offset */
5811 }
5812 else if (*end == '-')
5813 {
5814 ++end;
5815 *p = -getdigits(&end); /* negative offset */
5816 }
5817 }
5818 if (*end != ',')
5819 break;
5820 ++end;
5821 }
5822 }
5823 } while (idx >= 0);
5824
Bram Moolenaar1c465442017-03-12 20:10:05 +01005825 if (!ends_excmd(*end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005826 {
5827 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5828 return NULL;
5829 }
5830 return skipwhite(end);
5831}
5832
5833/*
5834 * Handle ":syntax sync .." command.
5835 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005836 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005837syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005838{
5839 char_u *arg_start = eap->arg;
5840 char_u *arg_end;
5841 char_u *key = NULL;
5842 char_u *next_arg;
5843 int illegal = FALSE;
5844 int finished = FALSE;
5845 long n;
5846 char_u *cpo_save;
5847
5848 if (ends_excmd(*arg_start))
5849 {
5850 syn_cmd_list(eap, TRUE);
5851 return;
5852 }
5853
5854 while (!ends_excmd(*arg_start))
5855 {
5856 arg_end = skiptowhite(arg_start);
5857 next_arg = skipwhite(arg_end);
5858 vim_free(key);
5859 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5860 if (STRCMP(key, "CCOMMENT") == 0)
5861 {
5862 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005863 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005864 if (!ends_excmd(*next_arg))
5865 {
5866 arg_end = skiptowhite(next_arg);
5867 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005868 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005869 (int)(arg_end - next_arg));
5870 next_arg = skipwhite(arg_end);
5871 }
5872 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005873 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005874 }
5875 else if ( STRNCMP(key, "LINES", 5) == 0
5876 || STRNCMP(key, "MINLINES", 8) == 0
5877 || STRNCMP(key, "MAXLINES", 8) == 0
5878 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5879 {
5880 if (key[4] == 'S')
5881 arg_end = key + 6;
5882 else if (key[0] == 'L')
5883 arg_end = key + 11;
5884 else
5885 arg_end = key + 9;
5886 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5887 {
5888 illegal = TRUE;
5889 break;
5890 }
5891 n = getdigits(&arg_end);
5892 if (!eap->skip)
5893 {
5894 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005895 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005896 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005897 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005898 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005899 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005900 }
5901 }
5902 else if (STRCMP(key, "FROMSTART") == 0)
5903 {
5904 if (!eap->skip)
5905 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005906 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5907 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005908 }
5909 }
5910 else if (STRCMP(key, "LINECONT") == 0)
5911 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005912 if (*next_arg == NUL) /* missing pattern */
5913 {
5914 illegal = TRUE;
5915 break;
5916 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005917 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005918 {
5919 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5920 finished = TRUE;
5921 break;
5922 }
5923 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5924 if (*arg_end != *next_arg) /* end delimiter not found */
5925 {
5926 illegal = TRUE;
5927 break;
5928 }
5929
5930 if (!eap->skip)
5931 {
5932 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005933 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005934 (int)(arg_end - next_arg - 1))) == NULL)
5935 {
5936 finished = TRUE;
5937 break;
5938 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005939 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005940
5941 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5942 cpo_save = p_cpo;
5943 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005944 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005945 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005946 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005947#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005948 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5949#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005950
Bram Moolenaar860cae12010-06-05 23:22:07 +02005951 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005952 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01005953 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005954 finished = TRUE;
5955 break;
5956 }
5957 }
5958 next_arg = skipwhite(arg_end + 1);
5959 }
5960 else
5961 {
5962 eap->arg = next_arg;
5963 if (STRCMP(key, "MATCH") == 0)
5964 syn_cmd_match(eap, TRUE);
5965 else if (STRCMP(key, "REGION") == 0)
5966 syn_cmd_region(eap, TRUE);
5967 else if (STRCMP(key, "CLEAR") == 0)
5968 syn_cmd_clear(eap, TRUE);
5969 else
5970 illegal = TRUE;
5971 finished = TRUE;
5972 break;
5973 }
5974 arg_start = next_arg;
5975 }
5976 vim_free(key);
5977 if (illegal)
5978 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5979 else if (!finished)
5980 {
5981 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005982 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005983 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005984 }
5985}
5986
5987/*
5988 * Convert a line of highlight group names into a list of group ID numbers.
5989 * "arg" should point to the "contains" or "nextgroup" keyword.
5990 * "arg" is advanced to after the last group name.
5991 * Careful: the argument is modified (NULs added).
5992 * returns FAIL for some error, OK for success.
5993 */
5994 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005995get_id_list(
5996 char_u **arg,
5997 int keylen, /* length of keyword */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005998 short **list, /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00005999 NULL, the list is silently skipped! */
Bram Moolenaarde318c52017-01-17 16:27:10 +01006000 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006001{
6002 char_u *p = NULL;
6003 char_u *end;
6004 int round;
6005 int count;
6006 int total_count = 0;
6007 short *retval = NULL;
6008 char_u *name;
6009 regmatch_T regmatch;
6010 int id;
6011 int i;
6012 int failed = FALSE;
6013
6014 /*
6015 * We parse the list twice:
6016 * round == 1: count the number of items, allocate the array.
6017 * round == 2: fill the array with the items.
6018 * In round 1 new groups may be added, causing the number of items to
6019 * grow when a regexp is used. In that case round 1 is done once again.
6020 */
6021 for (round = 1; round <= 2; ++round)
6022 {
6023 /*
6024 * skip "contains"
6025 */
6026 p = skipwhite(*arg + keylen);
6027 if (*p != '=')
6028 {
6029 EMSG2(_("E405: Missing equal sign: %s"), *arg);
6030 break;
6031 }
6032 p = skipwhite(p + 1);
6033 if (ends_excmd(*p))
6034 {
6035 EMSG2(_("E406: Empty argument: %s"), *arg);
6036 break;
6037 }
6038
6039 /*
6040 * parse the arguments after "contains"
6041 */
6042 count = 0;
6043 while (!ends_excmd(*p))
6044 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01006045 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006046 ;
6047 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
6048 if (name == NULL)
6049 {
6050 failed = TRUE;
6051 break;
6052 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006053 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006054 if ( STRCMP(name + 1, "ALLBUT") == 0
6055 || STRCMP(name + 1, "ALL") == 0
6056 || STRCMP(name + 1, "TOP") == 0
6057 || STRCMP(name + 1, "CONTAINED") == 0)
6058 {
6059 if (TOUPPER_ASC(**arg) != 'C')
6060 {
6061 EMSG2(_("E407: %s not allowed here"), name + 1);
6062 failed = TRUE;
6063 vim_free(name);
6064 break;
6065 }
6066 if (count != 0)
6067 {
Bram Moolenaard7a96152017-01-22 15:28:55 +01006068 EMSG2(_("E408: %s must be first in contains list"),
6069 name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006070 failed = TRUE;
6071 vim_free(name);
6072 break;
6073 }
6074 if (name[1] == 'A')
6075 id = SYNID_ALLBUT;
6076 else if (name[1] == 'T')
6077 id = SYNID_TOP;
6078 else
6079 id = SYNID_CONTAINED;
6080 id += current_syn_inc_tag;
6081 }
6082 else if (name[1] == '@')
6083 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01006084 if (skip)
6085 id = -1;
6086 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01006087 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006088 }
6089 else
6090 {
6091 /*
6092 * Handle full group name.
6093 */
6094 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6095 id = syn_check_group(name + 1, (int)(end - p));
6096 else
6097 {
6098 /*
6099 * Handle match of regexp with group names.
6100 */
6101 *name = '^';
6102 STRCAT(name, "$");
6103 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6104 if (regmatch.regprog == NULL)
6105 {
6106 failed = TRUE;
6107 vim_free(name);
6108 break;
6109 }
6110
6111 regmatch.rm_ic = TRUE;
6112 id = 0;
6113 for (i = highlight_ga.ga_len; --i >= 0; )
6114 {
6115 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6116 (colnr_T)0))
6117 {
6118 if (round == 2)
6119 {
6120 /* Got more items than expected; can happen
6121 * when adding items that match:
6122 * "contains=a.*b,axb".
6123 * Go back to first round */
6124 if (count >= total_count)
6125 {
6126 vim_free(retval);
6127 round = 1;
6128 }
6129 else
6130 retval[count] = i + 1;
6131 }
6132 ++count;
6133 id = -1; /* remember that we found one */
6134 }
6135 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006136 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006137 }
6138 }
6139 vim_free(name);
6140 if (id == 0)
6141 {
6142 EMSG2(_("E409: Unknown group name: %s"), p);
6143 failed = TRUE;
6144 break;
6145 }
6146 if (id > 0)
6147 {
6148 if (round == 2)
6149 {
6150 /* Got more items than expected, go back to first round */
6151 if (count >= total_count)
6152 {
6153 vim_free(retval);
6154 round = 1;
6155 }
6156 else
6157 retval[count] = id;
6158 }
6159 ++count;
6160 }
6161 p = skipwhite(end);
6162 if (*p != ',')
6163 break;
6164 p = skipwhite(p + 1); /* skip comma in between arguments */
6165 }
6166 if (failed)
6167 break;
6168 if (round == 1)
6169 {
6170 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6171 if (retval == NULL)
6172 break;
6173 retval[count] = 0; /* zero means end of the list */
6174 total_count = count;
6175 }
6176 }
6177
6178 *arg = p;
6179 if (failed || retval == NULL)
6180 {
6181 vim_free(retval);
6182 return FAIL;
6183 }
6184
6185 if (*list == NULL)
6186 *list = retval;
6187 else
6188 vim_free(retval); /* list already found, don't overwrite it */
6189
6190 return OK;
6191}
6192
6193/*
6194 * Make a copy of an ID list.
6195 */
6196 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006197copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006198{
6199 int len;
6200 int count;
6201 short *retval;
6202
6203 if (list == NULL)
6204 return NULL;
6205
6206 for (count = 0; list[count]; ++count)
6207 ;
6208 len = (count + 1) * sizeof(short);
6209 retval = (short *)alloc((unsigned)len);
6210 if (retval != NULL)
6211 mch_memmove(retval, list, (size_t)len);
6212
6213 return retval;
6214}
6215
6216/*
6217 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6218 * "cur_si" can be NULL if not checking the "containedin" list.
6219 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6220 * the current item.
6221 * This function is called very often, keep it fast!!
6222 */
6223 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006224in_id_list(
6225 stateitem_T *cur_si, /* current item or NULL */
6226 short *list, /* id list */
6227 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6228 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006229{
6230 int retval;
6231 short *scl_list;
6232 short item;
6233 short id = ssp->id;
6234 static int depth = 0;
6235 int r;
6236
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006237 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006238 if (cur_si != NULL && ssp->cont_in_list != NULL
6239 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006240 {
6241 /* Ignore transparent items without a contains argument. Double check
6242 * that we don't go back past the first one. */
6243 while ((cur_si->si_flags & HL_TRANS_CONT)
6244 && cur_si > (stateitem_T *)(current_state.ga_data))
6245 --cur_si;
6246 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6247 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006248 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6249 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006250 return TRUE;
6251 }
6252
6253 if (list == NULL)
6254 return FALSE;
6255
6256 /*
6257 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6258 * inside anything. Only allow not-contained groups.
6259 */
6260 if (list == ID_LIST_ALL)
6261 return !contained;
6262
6263 /*
6264 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6265 * contains list. We also require that "id" is at the same ":syn include"
6266 * level as the list.
6267 */
6268 item = *list;
6269 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6270 {
6271 if (item < SYNID_TOP)
6272 {
6273 /* ALL or ALLBUT: accept all groups in the same file */
6274 if (item - SYNID_ALLBUT != ssp->inc_tag)
6275 return FALSE;
6276 }
6277 else if (item < SYNID_CONTAINED)
6278 {
6279 /* TOP: accept all not-contained groups in the same file */
6280 if (item - SYNID_TOP != ssp->inc_tag || contained)
6281 return FALSE;
6282 }
6283 else
6284 {
6285 /* CONTAINED: accept all contained groups in the same file */
6286 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6287 return FALSE;
6288 }
6289 item = *++list;
6290 retval = FALSE;
6291 }
6292 else
6293 retval = TRUE;
6294
6295 /*
6296 * Return "retval" if id is in the contains list.
6297 */
6298 while (item != 0)
6299 {
6300 if (item == id)
6301 return retval;
6302 if (item >= SYNID_CLUSTER)
6303 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006304 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006305 /* restrict recursiveness to 30 to avoid an endless loop for a
6306 * cluster that includes itself (indirectly) */
6307 if (scl_list != NULL && depth < 30)
6308 {
6309 ++depth;
6310 r = in_id_list(NULL, scl_list, ssp, contained);
6311 --depth;
6312 if (r)
6313 return retval;
6314 }
6315 }
6316 item = *++list;
6317 }
6318 return !retval;
6319}
6320
6321struct subcommand
6322{
Bram Moolenaard99df422016-01-29 23:20:40 +01006323 char *name; /* subcommand name */
6324 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006325};
6326
6327static struct subcommand subcommands[] =
6328{
6329 {"case", syn_cmd_case},
6330 {"clear", syn_cmd_clear},
6331 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006332 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006333 {"enable", syn_cmd_enable},
6334 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006335 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006336 {"keyword", syn_cmd_keyword},
6337 {"list", syn_cmd_list},
6338 {"manual", syn_cmd_manual},
6339 {"match", syn_cmd_match},
6340 {"on", syn_cmd_on},
6341 {"off", syn_cmd_off},
6342 {"region", syn_cmd_region},
6343 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006344 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006345 {"sync", syn_cmd_sync},
6346 {"", syn_cmd_list},
6347 {NULL, NULL}
6348};
6349
6350/*
6351 * ":syntax".
6352 * This searches the subcommands[] table for the subcommand name, and calls a
6353 * syntax_subcommand() function to do the rest.
6354 */
6355 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006356ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006357{
6358 char_u *arg = eap->arg;
6359 char_u *subcmd_end;
6360 char_u *subcmd_name;
6361 int i;
6362
6363 syn_cmdlinep = eap->cmdlinep;
6364
6365 /* isolate subcommand name */
6366 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6367 ;
6368 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6369 if (subcmd_name != NULL)
6370 {
6371 if (eap->skip) /* skip error messages for all subcommands */
6372 ++emsg_skip;
6373 for (i = 0; ; ++i)
6374 {
6375 if (subcommands[i].name == NULL)
6376 {
6377 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6378 break;
6379 }
6380 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6381 {
6382 eap->arg = skipwhite(subcmd_end);
6383 (subcommands[i].func)(eap, FALSE);
6384 break;
6385 }
6386 }
6387 vim_free(subcmd_name);
6388 if (eap->skip)
6389 --emsg_skip;
6390 }
6391}
6392
Bram Moolenaar860cae12010-06-05 23:22:07 +02006393 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006394ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006395{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006396 char_u *old_value;
6397 char_u *new_value;
6398
Bram Moolenaar860cae12010-06-05 23:22:07 +02006399 if (curwin->w_s == &curwin->w_buffer->b_s)
6400 {
6401 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6402 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006403 hash_init(&curwin->w_s->b_keywtab);
6404 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006405#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006406 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006407 curwin->w_p_spell = FALSE; /* No spell checking */
6408 clear_string_option(&curwin->w_s->b_p_spc);
6409 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006410 clear_string_option(&curwin->w_s->b_p_spl);
6411#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006412 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006413 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006414
6415 /* save value of b:current_syntax */
6416 old_value = get_var_value((char_u *)"b:current_syntax");
6417 if (old_value != NULL)
6418 old_value = vim_strsave(old_value);
6419
Bram Moolenaard1413d92016-03-02 21:51:56 +01006420#ifdef FEAT_AUTOCMD
Bram Moolenaar1950c352010-06-06 15:21:10 +02006421 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6422 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006423 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaard1413d92016-03-02 21:51:56 +01006424#endif
Bram Moolenaar1950c352010-06-06 15:21:10 +02006425
6426 /* move value of b:current_syntax to w:current_syntax */
6427 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006428 if (new_value != NULL)
6429 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006430
6431 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006432 if (old_value == NULL)
6433 do_unlet((char_u *)"b:current_syntax", TRUE);
6434 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006435 {
6436 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6437 vim_free(old_value);
6438 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006439}
6440
6441 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006442syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006443{
6444 return (win->w_s->b_syn_patterns.ga_len != 0
6445 || win->w_s->b_syn_clusters.ga_len != 0
6446 || win->w_s->b_keywtab.ht_used > 0
6447 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006448}
6449
6450#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6451
6452static enum
6453{
6454 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006455 EXP_CASE, /* expand ":syn case" arguments */
6456 EXP_SPELL, /* expand ":syn spell" arguments */
6457 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006458} expand_what;
6459
Bram Moolenaar4f688582007-07-24 12:34:30 +00006460/*
6461 * Reset include_link, include_default, include_none to 0.
6462 * Called when we are done expanding.
6463 */
6464 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006465reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006466{
6467 include_link = include_default = include_none = 0;
6468}
6469
6470/*
6471 * Handle command line completion for :match and :echohl command: Add "None"
6472 * as highlight group.
6473 */
6474 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006475set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006476{
6477 xp->xp_context = EXPAND_HIGHLIGHT;
6478 xp->xp_pattern = arg;
6479 include_none = 1;
6480}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006481
6482/*
6483 * Handle command line completion for :syntax command.
6484 */
6485 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006486set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006487{
6488 char_u *p;
6489
6490 /* Default: expand subcommands */
6491 xp->xp_context = EXPAND_SYNTAX;
6492 expand_what = EXP_SUBCMD;
6493 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006494 include_link = 0;
6495 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006496
6497 /* (part of) subcommand already typed */
6498 if (*arg != NUL)
6499 {
6500 p = skiptowhite(arg);
6501 if (*p != NUL) /* past first word */
6502 {
6503 xp->xp_pattern = skipwhite(p);
6504 if (*skiptowhite(xp->xp_pattern) != NUL)
6505 xp->xp_context = EXPAND_NOTHING;
6506 else if (STRNICMP(arg, "case", p - arg) == 0)
6507 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006508 else if (STRNICMP(arg, "spell", p - arg) == 0)
6509 expand_what = EXP_SPELL;
6510 else if (STRNICMP(arg, "sync", p - arg) == 0)
6511 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006512 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6513 || STRNICMP(arg, "region", p - arg) == 0
6514 || STRNICMP(arg, "match", p - arg) == 0
6515 || STRNICMP(arg, "list", p - arg) == 0)
6516 xp->xp_context = EXPAND_HIGHLIGHT;
6517 else
6518 xp->xp_context = EXPAND_NOTHING;
6519 }
6520 }
6521}
6522
Bram Moolenaar071d4272004-06-13 20:20:40 +00006523/*
6524 * Function given to ExpandGeneric() to obtain the list syntax names for
6525 * expansion.
6526 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006527 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006528get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006529{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006530 switch (expand_what)
6531 {
6532 case EXP_SUBCMD:
6533 return (char_u *)subcommands[idx].name;
6534 case EXP_CASE:
6535 {
6536 static char *case_args[] = {"match", "ignore", NULL};
6537 return (char_u *)case_args[idx];
6538 }
6539 case EXP_SPELL:
6540 {
6541 static char *spell_args[] =
6542 {"toplevel", "notoplevel", "default", NULL};
6543 return (char_u *)spell_args[idx];
6544 }
6545 case EXP_SYNC:
6546 {
6547 static char *sync_args[] =
6548 {"ccomment", "clear", "fromstart",
6549 "linebreaks=", "linecont", "lines=", "match",
6550 "maxlines=", "minlines=", "region", NULL};
6551 return (char_u *)sync_args[idx];
6552 }
6553 }
6554 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006555}
6556
6557#endif /* FEAT_CMDL_COMPL */
6558
Bram Moolenaar071d4272004-06-13 20:20:40 +00006559/*
6560 * Function called for expression evaluation: get syntax ID at file position.
6561 */
6562 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006563syn_get_id(
6564 win_T *wp,
6565 long lnum,
6566 colnr_T col,
6567 int trans, /* remove transparency */
6568 int *spellp, /* return: can do spell checking */
6569 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006570{
6571 /* When the position is not after the current position and in the same
6572 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006573 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006574 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006575 || col < current_col)
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006576 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006577 else if (wp->w_buffer == syn_buf
6578 && lnum == current_lnum
6579 && col > current_col)
6580 /* next_match may not be correct when moving around, e.g. with the
6581 * "skip" expression in searchpair() */
6582 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006583
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006584 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006585
6586 return (trans ? current_trans_id : current_id);
6587}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006588
Bram Moolenaar860cae12010-06-05 23:22:07 +02006589#if defined(FEAT_CONCEAL) || defined(PROTO)
6590/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006591 * Get extra information about the syntax item. Must be called right after
6592 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006593 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006594 * Returns the current flags.
6595 */
6596 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006597get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006598{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006599 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006600 return current_flags;
6601}
6602
6603/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006604 * Return conceal substitution character
6605 */
6606 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006607syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006608{
6609 return current_sub_char;
6610}
6611#endif
6612
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006613#if defined(FEAT_EVAL) || defined(PROTO)
6614/*
6615 * Return the syntax ID at position "i" in the current stack.
6616 * The caller must have called syn_get_id() before to fill the stack.
6617 * Returns -1 when "i" is out of range.
6618 */
6619 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006620syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006621{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006622 if (i >= current_state.ga_len)
6623 {
6624 /* Need to invalidate the state, because we didn't properly finish it
6625 * for the last character, "keep_state" was TRUE. */
6626 invalidate_current_state();
6627 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006628 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006629 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006630 return CUR_STATE(i).si_id;
6631}
6632#endif
6633
Bram Moolenaar071d4272004-06-13 20:20:40 +00006634#if defined(FEAT_FOLDING) || defined(PROTO)
6635/*
6636 * Function called to get folding level for line "lnum" in window "wp".
6637 */
6638 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006639syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006640{
6641 int level = 0;
6642 int i;
6643
6644 /* Return quickly when there are no fold items at all. */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006645 if (wp->w_s->b_syn_folditems != 0
6646 && !wp->w_s->b_syn_error
6647# ifdef SYN_TIME_LIMIT
6648 && !wp->w_s->b_syn_slow
6649# endif
6650 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006651 {
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006652 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006653
6654 for (i = 0; i < current_state.ga_len; ++i)
6655 if (CUR_STATE(i).si_flags & HL_FOLD)
6656 ++level;
6657 }
6658 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006659 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006660 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006661 if (level < 0)
6662 level = 0;
6663 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006664 return level;
6665}
6666#endif
6667
Bram Moolenaar01615492015-02-03 13:00:38 +01006668#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006669/*
6670 * ":syntime".
6671 */
6672 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006673ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006674{
6675 if (STRCMP(eap->arg, "on") == 0)
6676 syn_time_on = TRUE;
6677 else if (STRCMP(eap->arg, "off") == 0)
6678 syn_time_on = FALSE;
6679 else if (STRCMP(eap->arg, "clear") == 0)
6680 syntime_clear();
6681 else if (STRCMP(eap->arg, "report") == 0)
6682 syntime_report();
6683 else
6684 EMSG2(_(e_invarg2), eap->arg);
6685}
6686
6687 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006688syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006689{
6690 profile_zero(&st->total);
6691 profile_zero(&st->slowest);
6692 st->count = 0;
6693 st->match = 0;
6694}
6695
6696/*
6697 * Clear the syntax timing for the current buffer.
6698 */
6699 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006700syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006701{
6702 int idx;
6703 synpat_T *spp;
6704
6705 if (!syntax_present(curwin))
6706 {
6707 MSG(_(msg_no_items));
6708 return;
6709 }
6710 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6711 {
6712 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6713 syn_clear_time(&spp->sp_time);
6714 }
6715}
6716
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006717#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6718/*
6719 * Function given to ExpandGeneric() to obtain the possible arguments of the
6720 * ":syntime {on,off,clear,report}" command.
6721 */
6722 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006723get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006724{
6725 switch (idx)
6726 {
6727 case 0: return (char_u *)"on";
6728 case 1: return (char_u *)"off";
6729 case 2: return (char_u *)"clear";
6730 case 3: return (char_u *)"report";
6731 }
6732 return NULL;
6733}
6734#endif
6735
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006736typedef struct
6737{
6738 proftime_T total;
6739 int count;
6740 int match;
6741 proftime_T slowest;
6742 proftime_T average;
6743 int id;
6744 char_u *pattern;
6745} time_entry_T;
6746
6747 static int
6748#ifdef __BORLANDC__
6749_RTLENTRYF
6750#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006751syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006752{
6753 const time_entry_T *s1 = v1;
6754 const time_entry_T *s2 = v2;
6755
6756 return profile_cmp(&s1->total, &s2->total);
6757}
6758
6759/*
6760 * Clear the syntax timing for the current buffer.
6761 */
6762 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006763syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006764{
6765 int idx;
6766 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006767# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006768 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006769# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006770 int len;
6771 proftime_T total_total;
6772 int total_count = 0;
6773 garray_T ga;
6774 time_entry_T *p;
6775
6776 if (!syntax_present(curwin))
6777 {
6778 MSG(_(msg_no_items));
6779 return;
6780 }
6781
6782 ga_init2(&ga, sizeof(time_entry_T), 50);
6783 profile_zero(&total_total);
6784 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6785 {
6786 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6787 if (spp->sp_time.count > 0)
6788 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006789 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006790 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6791 p->total = spp->sp_time.total;
6792 profile_add(&total_total, &spp->sp_time.total);
6793 p->count = spp->sp_time.count;
6794 p->match = spp->sp_time.match;
6795 total_count += spp->sp_time.count;
6796 p->slowest = spp->sp_time.slowest;
6797# ifdef FEAT_FLOAT
6798 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6799 p->average = tm;
6800# endif
6801 p->id = spp->sp_syn.id;
6802 p->pattern = spp->sp_pattern;
6803 ++ga.ga_len;
6804 }
6805 }
6806
Bram Moolenaara2162552017-01-08 17:46:20 +01006807 /* Sort on total time. Skip if there are no items to avoid passing NULL
6808 * pointer to qsort(). */
6809 if (ga.ga_len > 1)
6810 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006811 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006812
6813 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6814 MSG_PUTS("\n");
6815 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6816 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006817 p = ((time_entry_T *)ga.ga_data) + idx;
6818
6819 MSG_PUTS(profile_msg(&p->total));
6820 MSG_PUTS(" "); /* make sure there is always a separating space */
6821 msg_advance(13);
6822 msg_outnum(p->count);
6823 MSG_PUTS(" ");
6824 msg_advance(20);
6825 msg_outnum(p->match);
6826 MSG_PUTS(" ");
6827 msg_advance(26);
6828 MSG_PUTS(profile_msg(&p->slowest));
6829 MSG_PUTS(" ");
6830 msg_advance(38);
6831# ifdef FEAT_FLOAT
6832 MSG_PUTS(profile_msg(&p->average));
6833 MSG_PUTS(" ");
6834# endif
6835 msg_advance(50);
6836 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6837 MSG_PUTS(" ");
6838
6839 msg_advance(69);
6840 if (Columns < 80)
6841 len = 20; /* will wrap anyway */
6842 else
6843 len = Columns - 70;
6844 if (len > (int)STRLEN(p->pattern))
6845 len = (int)STRLEN(p->pattern);
6846 msg_outtrans_len(p->pattern, len);
6847 MSG_PUTS("\n");
6848 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006849 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006850 if (!got_int)
6851 {
6852 MSG_PUTS("\n");
6853 MSG_PUTS(profile_msg(&total_total));
6854 msg_advance(13);
6855 msg_outnum(total_count);
6856 MSG_PUTS("\n");
6857 }
6858}
6859#endif
6860
Bram Moolenaar071d4272004-06-13 20:20:40 +00006861#endif /* FEAT_SYN_HL */
6862
Bram Moolenaar071d4272004-06-13 20:20:40 +00006863/**************************************
6864 * Highlighting stuff *
6865 **************************************/
6866
6867/*
6868 * The default highlight groups. These are compiled-in for fast startup and
6869 * they still work when the runtime files can't be found.
6870 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006871 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6872 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006873 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006874#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006875# define CENT(a, b) b
6876#else
6877# define CENT(a, b) a
6878#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006879static char *(highlight_init_both[]) = {
6880 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6881 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6882 CENT("IncSearch term=reverse cterm=reverse",
6883 "IncSearch term=reverse cterm=reverse gui=reverse"),
6884 CENT("ModeMsg term=bold cterm=bold",
6885 "ModeMsg term=bold cterm=bold gui=bold"),
6886 CENT("NonText term=bold ctermfg=Blue",
6887 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6888 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6889 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6890 CENT("StatusLineNC term=reverse cterm=reverse",
6891 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
6892 "default link EndOfBuffer NonText",
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006893 CENT("VertSplit term=reverse cterm=reverse",
6894 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006895#ifdef FEAT_CLIPBOARD
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006896 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6897 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006898#endif
6899#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006900 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6901 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006902#endif
6903#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006904 CENT("PmenuSbar ctermbg=Grey",
6905 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006906#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006907 CENT("TabLineSel term=bold cterm=bold",
6908 "TabLineSel term=bold cterm=bold gui=bold"),
6909 CENT("TabLineFill term=reverse cterm=reverse",
6910 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00006911#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006912 "Cursor guibg=fg guifg=bg",
6913 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006914#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006915 "default link QuickFixLine Search",
6916 NULL
6917};
Bram Moolenaar071d4272004-06-13 20:20:40 +00006918
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006919/* Default colors only used with a light background. */
6920static char *(highlight_init_light[]) = {
6921 CENT("Directory term=bold ctermfg=DarkBlue",
6922 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6923 CENT("LineNr term=underline ctermfg=Brown",
6924 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6925 CENT("CursorLineNr term=bold ctermfg=Brown",
6926 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
6927 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6928 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6929 CENT("Question term=standout ctermfg=DarkGreen",
6930 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6931 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6932 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006933#ifdef FEAT_SPELL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006934 CENT("SpellBad term=reverse ctermbg=LightRed",
6935 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6936 CENT("SpellCap term=reverse ctermbg=LightBlue",
6937 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6938 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6939 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6940 CENT("SpellLocal term=underline ctermbg=Cyan",
6941 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006942#endif
6943#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006944 CENT("PmenuThumb ctermbg=Black",
6945 "PmenuThumb ctermbg=Black guibg=Black"),
6946 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6947 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6948 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6949 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006950#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006951 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6952 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6953 CENT("Title term=bold ctermfg=DarkMagenta",
6954 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6955 CENT("WarningMsg term=standout ctermfg=DarkRed",
6956 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006957#ifdef FEAT_WILDMENU
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006958 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6959 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006960#endif
6961#ifdef FEAT_FOLDING
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006962 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6963 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6964 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6965 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006966#endif
6967#ifdef FEAT_SIGNS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006968 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6969 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006970#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006971 CENT("Visual term=reverse",
6972 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006973#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006974 CENT("DiffAdd term=bold ctermbg=LightBlue",
6975 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6976 CENT("DiffChange term=bold ctermbg=LightMagenta",
6977 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6978 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6979 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006980#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006981 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6982 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006983#ifdef FEAT_SYN_HL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006984 CENT("CursorColumn term=reverse ctermbg=LightGrey",
6985 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
6986 CENT("CursorLine term=underline cterm=underline",
6987 "CursorLine term=underline cterm=underline guibg=Grey90"),
6988 CENT("ColorColumn term=reverse ctermbg=LightRed",
6989 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006990#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006991#ifdef FEAT_CONCEAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006992 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6993 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02006994#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006995#ifdef FEAT_AUTOCMD
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006996 CENT("MatchParen term=reverse ctermbg=Cyan",
6997 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006998#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006999#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007000 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007001#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007002#ifdef FEAT_TERMINAL
7003 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
7004 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
7005 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
7006 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
7007#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007008#ifdef FEAT_MENU
7009 CENT("ToolbarLine term=underline ctermbg=LightGrey",
7010 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
7011 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02007012 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007013#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007014 NULL
7015};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007016
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007017/* Default colors only used with a dark background. */
7018static char *(highlight_init_dark[]) = {
7019 CENT("Directory term=bold ctermfg=LightCyan",
7020 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
7021 CENT("LineNr term=underline ctermfg=Yellow",
7022 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
7023 CENT("CursorLineNr term=bold ctermfg=Yellow",
7024 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
7025 CENT("MoreMsg term=bold ctermfg=LightGreen",
7026 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
7027 CENT("Question term=standout ctermfg=LightGreen",
7028 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
7029 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
7030 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
7031 CENT("SpecialKey term=bold ctermfg=LightBlue",
7032 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007033#ifdef FEAT_SPELL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007034 CENT("SpellBad term=reverse ctermbg=Red",
7035 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
7036 CENT("SpellCap term=reverse ctermbg=Blue",
7037 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
7038 CENT("SpellRare term=reverse ctermbg=Magenta",
7039 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
7040 CENT("SpellLocal term=underline ctermbg=Cyan",
7041 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007042#endif
7043#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007044 CENT("PmenuThumb ctermbg=White",
7045 "PmenuThumb ctermbg=White guibg=White"),
7046 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
7047 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
7048 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
7049 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007050#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007051 CENT("Title term=bold ctermfg=LightMagenta",
7052 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
7053 CENT("WarningMsg term=standout ctermfg=LightRed",
7054 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007055#ifdef FEAT_WILDMENU
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007056 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
7057 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007058#endif
7059#ifdef FEAT_FOLDING
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007060 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
7061 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
7062 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7063 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007064#endif
7065#ifdef FEAT_SIGNS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007066 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7067 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007068#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007069 CENT("Visual term=reverse",
7070 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007071#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007072 CENT("DiffAdd term=bold ctermbg=DarkBlue",
7073 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
7074 CENT("DiffChange term=bold ctermbg=DarkMagenta",
7075 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
7076 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
7077 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007078#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007079 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7080 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007081#ifdef FEAT_SYN_HL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007082 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
7083 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
7084 CENT("CursorLine term=underline cterm=underline",
7085 "CursorLine term=underline cterm=underline guibg=Grey40"),
7086 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7087 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007088#endif
7089#ifdef FEAT_AUTOCMD
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007090 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7091 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007092#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02007093#ifdef FEAT_CONCEAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007094 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7095 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02007096#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007097#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007098 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007099#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007100#ifdef FEAT_TERMINAL
7101 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
7102 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
7103 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
7104 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
7105#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007106#ifdef FEAT_MENU
7107 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02007108 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007109 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
7110 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
7111#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007112 NULL
7113};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007114
7115 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007116init_highlight(
7117 int both, /* include groups where 'bg' doesn't matter */
7118 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007119{
7120 int i;
7121 char **pp;
7122 static int had_both = FALSE;
7123#ifdef FEAT_EVAL
7124 char_u *p;
7125
7126 /*
7127 * Try finding the color scheme file. Used when a color file was loaded
7128 * and 'background' or 't_Co' is changed.
7129 */
7130 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007131 if (p != NULL)
7132 {
7133 /* The value of g:colors_name could be freed when sourcing the script,
7134 * making "p" invalid, so copy it. */
7135 char_u *copy_p = vim_strsave(p);
7136 int r;
7137
7138 if (copy_p != NULL)
7139 {
7140 r = load_colors(copy_p);
7141 vim_free(copy_p);
7142 if (r == OK)
7143 return;
7144 }
7145 }
7146
Bram Moolenaar071d4272004-06-13 20:20:40 +00007147#endif
7148
7149 /*
7150 * Didn't use a color file, use the compiled-in colors.
7151 */
7152 if (both)
7153 {
7154 had_both = TRUE;
7155 pp = highlight_init_both;
7156 for (i = 0; pp[i] != NULL; ++i)
7157 do_highlight((char_u *)pp[i], reset, TRUE);
7158 }
7159 else if (!had_both)
7160 /* Don't do anything before the call with both == TRUE from main().
7161 * Not everything has been setup then, and that call will overrule
7162 * everything anyway. */
7163 return;
7164
7165 if (*p_bg == 'l')
7166 pp = highlight_init_light;
7167 else
7168 pp = highlight_init_dark;
7169 for (i = 0; pp[i] != NULL; ++i)
7170 do_highlight((char_u *)pp[i], reset, TRUE);
7171
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007172 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007173 * depend on the number of colors available.
7174 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007175 * to avoid Statement highlighted text disappears.
7176 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007177 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007178 do_highlight((char_u *)(*p_bg == 'l'
7179 ? "Visual cterm=NONE ctermbg=LightGrey"
7180 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007181 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007182 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007183 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7184 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007185 if (*p_bg == 'l')
7186 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7187 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007188
Bram Moolenaar071d4272004-06-13 20:20:40 +00007189#ifdef FEAT_SYN_HL
7190 /*
7191 * If syntax highlighting is enabled load the highlighting for it.
7192 */
7193 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007194 {
7195 static int recursive = 0;
7196
7197 if (recursive >= 5)
7198 EMSG(_("E679: recursive loop loading syncolor.vim"));
7199 else
7200 {
7201 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007202 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007203 --recursive;
7204 }
7205 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007206#endif
7207}
7208
7209/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007210 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007211 * Return OK for success, FAIL for failure.
7212 */
7213 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007214load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007215{
7216 char_u *buf;
7217 int retval = FAIL;
7218 static int recursive = FALSE;
7219
7220 /* When being called recursively, this is probably because setting
7221 * 'background' caused the highlighting to be reloaded. This means it is
7222 * working, thus we should return OK. */
7223 if (recursive)
7224 return OK;
7225
7226 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007227 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007228 if (buf != NULL)
7229 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007230 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007231 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007232 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007233#ifdef FEAT_AUTOCMD
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007234 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007235#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007236 }
7237 recursive = FALSE;
7238
7239 return retval;
7240}
7241
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007242static char *(color_names[28]) = {
7243 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7244 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7245 "Gray", "Grey", "LightGray", "LightGrey",
7246 "DarkGray", "DarkGrey",
7247 "Blue", "LightBlue", "Green", "LightGreen",
7248 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7249 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7250 /* indices:
7251 * 0, 1, 2, 3,
7252 * 4, 5, 6, 7,
7253 * 8, 9, 10, 11,
7254 * 12, 13,
7255 * 14, 15, 16, 17,
7256 * 18, 19, 20, 21, 22,
7257 * 23, 24, 25, 26, 27 */
7258static int color_numbers_16[28] = {0, 1, 2, 3,
7259 4, 5, 6, 6,
7260 7, 7, 7, 7,
7261 8, 8,
7262 9, 9, 10, 10,
7263 11, 11, 12, 12, 13,
7264 13, 14, 14, 15, -1};
7265/* for xterm with 88 colors... */
7266static int color_numbers_88[28] = {0, 4, 2, 6,
7267 1, 5, 32, 72,
7268 84, 84, 7, 7,
7269 82, 82,
7270 12, 43, 10, 61,
7271 14, 63, 9, 74, 13,
7272 75, 11, 78, 15, -1};
7273/* for xterm with 256 colors... */
7274static int color_numbers_256[28] = {0, 4, 2, 6,
7275 1, 5, 130, 130,
7276 248, 248, 7, 7,
7277 242, 242,
7278 12, 81, 10, 121,
7279 14, 159, 9, 224, 13,
7280 225, 11, 229, 15, -1};
7281/* for terminals with less than 16 colors... */
7282static int color_numbers_8[28] = {0, 4, 2, 6,
7283 1, 5, 3, 3,
7284 7, 7, 7, 7,
7285 0+8, 0+8,
7286 4+8, 4+8, 2+8, 2+8,
7287 6+8, 6+8, 1+8, 1+8, 5+8,
7288 5+8, 3+8, 3+8, 7+8, -1};
7289
7290/*
7291 * Lookup the "cterm" value to be used for color with index "idx" in
7292 * color_names[].
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007293 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
7294 * colors, otherwise it will be unchanged.
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007295 */
7296 int
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007297lookup_color(int idx, int foreground, int *boldp)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007298{
7299 int color = color_numbers_16[idx];
7300 char_u *p;
7301
7302 /* Use the _16 table to check if it's a valid color name. */
7303 if (color < 0)
7304 return -1;
7305
7306 if (t_colors == 8)
7307 {
7308 /* t_Co is 8: use the 8 colors table */
7309#if defined(__QNXNTO__)
7310 color = color_numbers_8_qansi[idx];
7311#else
7312 color = color_numbers_8[idx];
7313#endif
7314 if (foreground)
7315 {
7316 /* set/reset bold attribute to get light foreground
7317 * colors (on some terminals, e.g. "linux") */
7318 if (color & 8)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007319 *boldp = TRUE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007320 else
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007321 *boldp = FALSE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007322 }
7323 color &= 7; /* truncate to 8 colors */
7324 }
7325 else if (t_colors == 16 || t_colors == 88
7326 || t_colors >= 256)
7327 {
7328 /*
7329 * Guess: if the termcap entry ends in 'm', it is
7330 * probably an xterm-like terminal. Use the changed
7331 * order for colors.
7332 */
7333 if (*T_CAF != NUL)
7334 p = T_CAF;
7335 else
7336 p = T_CSF;
7337 if (*p != NUL && (t_colors > 256
7338 || *(p + STRLEN(p) - 1) == 'm'))
7339 {
7340 if (t_colors == 88)
7341 color = color_numbers_88[idx];
7342 else if (t_colors >= 256)
7343 color = color_numbers_256[idx];
7344 else
7345 color = color_numbers_8[idx];
7346 }
Bram Moolenaarc9026092017-10-04 19:35:02 +02007347#ifdef FEAT_TERMRESPONSE
Bram Moolenaara0a6f272017-10-04 18:04:16 +02007348 if (t_colors >= 256 && color == 15 && is_mac_terminal)
7349 /* Terminal.app has a bug: 15 is light grey. Use white
7350 * from the color cube instead. */
7351 color = 231;
Bram Moolenaarc9026092017-10-04 19:35:02 +02007352#endif
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007353 }
7354 return color;
7355}
7356
Bram Moolenaar071d4272004-06-13 20:20:40 +00007357/*
7358 * Handle the ":highlight .." command.
7359 * When using ":hi clear" this is called recursively for each group with
7360 * "forceit" and "init" both TRUE.
7361 */
7362 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007363do_highlight(
7364 char_u *line,
7365 int forceit,
7366 int init) /* TRUE when called for initializing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007367{
7368 char_u *name_end;
7369 char_u *p;
7370 char_u *linep;
7371 char_u *key_start;
7372 char_u *arg_start;
7373 char_u *key = NULL, *arg = NULL;
7374 long i;
7375 int off;
7376 int len;
7377 int attr;
7378 int id;
7379 int idx;
Bram Moolenaar99433292017-09-08 12:37:47 +02007380 struct hl_group item_before;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007381 int did_change = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007382 int dodefault = FALSE;
7383 int doclear = FALSE;
7384 int dolink = FALSE;
7385 int error = FALSE;
7386 int color;
7387 int is_normal_group = FALSE; /* "Normal" group */
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01007388#ifdef FEAT_TERMINAL
7389 int is_terminal_group = FALSE; /* "Terminal" group */
7390#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007391#ifdef FEAT_GUI_X11
7392 int is_menu_group = FALSE; /* "Menu" group */
7393 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7394 int is_tooltip_group = FALSE; /* "Tooltip" group */
7395 int do_colors = FALSE; /* need to update colors? */
7396#else
7397# define is_menu_group 0
7398# define is_tooltip_group 0
7399#endif
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007400#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
7401 int did_highlight_changed = FALSE;
7402#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007403
7404 /*
7405 * If no argument, list current highlighting.
7406 */
7407 if (ends_excmd(*line))
7408 {
7409 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7410 /* TODO: only call when the group has attributes set */
7411 highlight_list_one((int)i);
7412 return;
7413 }
7414
7415 /*
7416 * Isolate the name.
7417 */
7418 name_end = skiptowhite(line);
7419 linep = skipwhite(name_end);
7420
7421 /*
7422 * Check for "default" argument.
7423 */
7424 if (STRNCMP(line, "default", name_end - line) == 0)
7425 {
7426 dodefault = TRUE;
7427 line = linep;
7428 name_end = skiptowhite(line);
7429 linep = skipwhite(name_end);
7430 }
7431
7432 /*
7433 * Check for "clear" or "link" argument.
7434 */
7435 if (STRNCMP(line, "clear", name_end - line) == 0)
7436 doclear = TRUE;
7437 if (STRNCMP(line, "link", name_end - line) == 0)
7438 dolink = TRUE;
7439
7440 /*
7441 * ":highlight {group-name}": list highlighting for one group.
7442 */
7443 if (!doclear && !dolink && ends_excmd(*linep))
7444 {
7445 id = syn_namen2id(line, (int)(name_end - line));
7446 if (id == 0)
7447 EMSG2(_("E411: highlight group not found: %s"), line);
7448 else
7449 highlight_list_one(id);
7450 return;
7451 }
7452
7453 /*
7454 * Handle ":highlight link {from} {to}" command.
7455 */
7456 if (dolink)
7457 {
7458 char_u *from_start = linep;
7459 char_u *from_end;
7460 char_u *to_start;
7461 char_u *to_end;
7462 int from_id;
7463 int to_id;
7464
7465 from_end = skiptowhite(from_start);
7466 to_start = skipwhite(from_end);
7467 to_end = skiptowhite(to_start);
7468
7469 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7470 {
7471 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7472 from_start);
7473 return;
7474 }
7475
7476 if (!ends_excmd(*skipwhite(to_end)))
7477 {
7478 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7479 return;
7480 }
7481
7482 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7483 if (STRNCMP(to_start, "NONE", 4) == 0)
7484 to_id = 0;
7485 else
7486 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7487
Bram Moolenaar414168d2017-09-10 15:21:55 +02007488 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007489 {
7490 /*
7491 * Don't allow a link when there already is some highlighting
7492 * for the group, unless '!' is used
7493 */
7494 if (to_id > 0 && !forceit && !init
7495 && hl_has_settings(from_id - 1, dodefault))
7496 {
7497 if (sourcing_name == NULL && !dodefault)
7498 EMSG(_("E414: group has settings, highlight link ignored"));
7499 }
Bram Moolenaar414168d2017-09-10 15:21:55 +02007500 else if (HL_TABLE()[from_id - 1].sg_link != to_id
Bram Moolenaar99433292017-09-08 12:37:47 +02007501#ifdef FEAT_EVAL
Bram Moolenaar414168d2017-09-10 15:21:55 +02007502 || HL_TABLE()[from_id - 1].sg_scriptID != current_SID
Bram Moolenaar99433292017-09-08 12:37:47 +02007503#endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007504 || HL_TABLE()[from_id - 1].sg_cleared)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007505 {
7506 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007507 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7508 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007509#ifdef FEAT_EVAL
Bram Moolenaar414168d2017-09-10 15:21:55 +02007510 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007511#endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007512 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007513 redraw_all_later(SOME_VALID);
Bram Moolenaar99433292017-09-08 12:37:47 +02007514
7515 /* Only call highlight_changed() once after multiple changes. */
7516 need_highlight_changed = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007517 }
7518 }
7519
Bram Moolenaar071d4272004-06-13 20:20:40 +00007520 return;
7521 }
7522
7523 if (doclear)
7524 {
7525 /*
7526 * ":highlight clear [group]" command.
7527 */
7528 line = linep;
7529 if (ends_excmd(*line))
7530 {
7531#ifdef FEAT_GUI
7532 /* First, we do not destroy the old values, but allocate the new
7533 * ones and update the display. THEN we destroy the old values.
7534 * If we destroy the old values first, then the old values
7535 * (such as GuiFont's or GuiFontset's) will still be displayed but
7536 * invalid because they were free'd.
7537 */
7538 if (gui.in_use)
7539 {
7540# ifdef FEAT_BEVAL_TIP
7541 gui_init_tooltip_font();
7542# endif
7543# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7544 gui_init_menu_font();
7545# endif
7546 }
7547# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7548 gui_mch_def_colors();
7549# endif
7550# ifdef FEAT_GUI_X11
7551# ifdef FEAT_MENU
7552
7553 /* This only needs to be done when there is no Menu highlight
7554 * group defined by default, which IS currently the case.
7555 */
7556 gui_mch_new_menu_colors();
7557# endif
7558 if (gui.in_use)
7559 {
7560 gui_new_scrollbar_colors();
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01007561# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007562 gui_mch_new_tooltip_colors();
7563# endif
7564# ifdef FEAT_MENU
7565 gui_mch_new_menu_font();
7566# endif
7567 }
7568# endif
7569
7570 /* Ok, we're done allocating the new default graphics items.
7571 * The screen should already be refreshed at this point.
7572 * It is now Ok to clear out the old data.
7573 */
7574#endif
7575#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007576 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007577#endif
7578 restore_cterm_colors();
7579
7580 /*
7581 * Clear all default highlight groups and load the defaults.
7582 */
7583 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7584 highlight_clear(idx);
7585 init_highlight(TRUE, TRUE);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007586#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007587 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007588 highlight_gui_started();
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007589 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007590#endif
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007591 highlight_changed();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007592 redraw_later_clear();
7593 return;
7594 }
7595 name_end = skiptowhite(line);
7596 linep = skipwhite(name_end);
7597 }
7598
7599 /*
7600 * Find the group name in the table. If it does not exist yet, add it.
7601 */
7602 id = syn_check_group(line, (int)(name_end - line));
7603 if (id == 0) /* failed (out of memory) */
7604 return;
7605 idx = id - 1; /* index is ID minus one */
7606
7607 /* Return if "default" was used and the group already has settings. */
7608 if (dodefault && hl_has_settings(idx, TRUE))
7609 return;
7610
Bram Moolenaar99433292017-09-08 12:37:47 +02007611 /* Make a copy so we can check if any attribute actually changed. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007612 item_before = HL_TABLE()[idx];
Bram Moolenaar99433292017-09-08 12:37:47 +02007613
Bram Moolenaar414168d2017-09-10 15:21:55 +02007614 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007615 is_normal_group = TRUE;
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01007616#ifdef FEAT_TERMINAL
7617 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TERMINAL") == 0)
7618 is_terminal_group = TRUE;
7619#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007620#ifdef FEAT_GUI_X11
Bram Moolenaar414168d2017-09-10 15:21:55 +02007621 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007622 is_menu_group = TRUE;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007623 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007624 is_scrollbar_group = TRUE;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007625 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007626 is_tooltip_group = TRUE;
7627#endif
7628
7629 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7630 if (doclear || (forceit && init))
7631 {
7632 highlight_clear(idx);
7633 if (!doclear)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007634 HL_TABLE()[idx].sg_set = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007635 }
7636
7637 if (!doclear)
7638 while (!ends_excmd(*linep))
7639 {
7640 key_start = linep;
7641 if (*linep == '=')
7642 {
7643 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7644 error = TRUE;
7645 break;
7646 }
7647
7648 /*
7649 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7650 * "guibg").
7651 */
Bram Moolenaar1c465442017-03-12 20:10:05 +01007652 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00007653 ++linep;
7654 vim_free(key);
7655 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7656 if (key == NULL)
7657 {
7658 error = TRUE;
7659 break;
7660 }
7661 linep = skipwhite(linep);
7662
7663 if (STRCMP(key, "NONE") == 0)
7664 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007665 if (!init || HL_TABLE()[idx].sg_set == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007666 {
7667 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007668 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007669 highlight_clear(idx);
7670 }
7671 continue;
7672 }
7673
7674 /*
7675 * Check for the equal sign.
7676 */
7677 if (*linep != '=')
7678 {
7679 EMSG2(_("E416: missing equal sign: %s"), key_start);
7680 error = TRUE;
7681 break;
7682 }
7683 ++linep;
7684
7685 /*
7686 * Isolate the argument.
7687 */
7688 linep = skipwhite(linep);
7689 if (*linep == '\'') /* guifg='color name' */
7690 {
7691 arg_start = ++linep;
7692 linep = vim_strchr(linep, '\'');
7693 if (linep == NULL)
7694 {
7695 EMSG2(_(e_invarg2), key_start);
7696 error = TRUE;
7697 break;
7698 }
7699 }
7700 else
7701 {
7702 arg_start = linep;
7703 linep = skiptowhite(linep);
7704 }
7705 if (linep == arg_start)
7706 {
7707 EMSG2(_("E417: missing argument: %s"), key_start);
7708 error = TRUE;
7709 break;
7710 }
7711 vim_free(arg);
7712 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7713 if (arg == NULL)
7714 {
7715 error = TRUE;
7716 break;
7717 }
7718 if (*linep == '\'')
7719 ++linep;
7720
7721 /*
7722 * Store the argument.
7723 */
7724 if ( STRCMP(key, "TERM") == 0
7725 || STRCMP(key, "CTERM") == 0
7726 || STRCMP(key, "GUI") == 0)
7727 {
7728 attr = 0;
7729 off = 0;
7730 while (arg[off] != NUL)
7731 {
7732 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7733 {
7734 len = (int)STRLEN(hl_name_table[i]);
7735 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7736 {
7737 attr |= hl_attr_table[i];
7738 off += len;
7739 break;
7740 }
7741 }
7742 if (i < 0)
7743 {
7744 EMSG2(_("E418: Illegal value: %s"), arg);
7745 error = TRUE;
7746 break;
7747 }
7748 if (arg[off] == ',') /* another one follows */
7749 ++off;
7750 }
7751 if (error)
7752 break;
7753 if (*key == 'T')
7754 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007755 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007756 {
7757 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007758 HL_TABLE()[idx].sg_set |= SG_TERM;
7759 HL_TABLE()[idx].sg_term = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007760 }
7761 }
7762 else if (*key == 'C')
7763 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007764 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007765 {
7766 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007767 HL_TABLE()[idx].sg_set |= SG_CTERM;
7768 HL_TABLE()[idx].sg_cterm = attr;
7769 HL_TABLE()[idx].sg_cterm_bold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007770 }
7771 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007772#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007773 else
7774 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007775 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007776 {
7777 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007778 HL_TABLE()[idx].sg_set |= SG_GUI;
7779 HL_TABLE()[idx].sg_gui = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007780 }
7781 }
7782#endif
7783 }
7784 else if (STRCMP(key, "FONT") == 0)
7785 {
7786 /* in non-GUI fonts are simply ignored */
7787#ifdef FEAT_GUI
Bram Moolenaar414168d2017-09-10 15:21:55 +02007788 if (HL_TABLE()[idx].sg_font_name != NULL
7789 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
Bram Moolenaar99433292017-09-08 12:37:47 +02007790 {
7791 /* Font name didn't change, ignore. */
7792 }
7793 else if (!gui.shell_created)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007794 {
7795 /* GUI not started yet, always accept the name. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007796 vim_free(HL_TABLE()[idx].sg_font_name);
7797 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007798 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007799 }
7800 else
7801 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007802 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007803# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007804 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007805# endif
7806 /* First, save the current font/fontset.
7807 * Then try to allocate the font/fontset.
Bram Moolenaar414168d2017-09-10 15:21:55 +02007808 * If the allocation fails, HL_TABLE()[idx].sg_font OR
Bram Moolenaar071d4272004-06-13 20:20:40 +00007809 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7810 */
7811
Bram Moolenaar414168d2017-09-10 15:21:55 +02007812 HL_TABLE()[idx].sg_font = NOFONT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007813# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007814 HL_TABLE()[idx].sg_fontset = NOFONTSET;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007815# endif
7816 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007817 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007818
7819# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007820 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007821 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007822 /* New fontset was accepted. Free the old one, if there
7823 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007824 gui_mch_free_fontset(temp_sg_fontset);
Bram Moolenaar414168d2017-09-10 15:21:55 +02007825 vim_free(HL_TABLE()[idx].sg_font_name);
7826 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007827 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007828 }
7829 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007830 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007831# endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007832 if (HL_TABLE()[idx].sg_font != NOFONT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007833 {
7834 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007835 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007836 gui_mch_free_font(temp_sg_font);
Bram Moolenaar414168d2017-09-10 15:21:55 +02007837 vim_free(HL_TABLE()[idx].sg_font_name);
7838 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007839 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007840 }
7841 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007842 HL_TABLE()[idx].sg_font = temp_sg_font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007843 }
7844#endif
7845 }
7846 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7847 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007848 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007849 {
7850 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007851 HL_TABLE()[idx].sg_set |= SG_CTERM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007852
7853 /* When setting the foreground color, and previously the "bold"
7854 * flag was set for a light color, reset it now */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007855 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007856 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007857 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7858 HL_TABLE()[idx].sg_cterm_bold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007859 }
7860
7861 if (VIM_ISDIGIT(*arg))
7862 color = atoi((char *)arg);
7863 else if (STRICMP(arg, "fg") == 0)
7864 {
7865 if (cterm_normal_fg_color)
7866 color = cterm_normal_fg_color - 1;
7867 else
7868 {
7869 EMSG(_("E419: FG color unknown"));
7870 error = TRUE;
7871 break;
7872 }
7873 }
7874 else if (STRICMP(arg, "bg") == 0)
7875 {
7876 if (cterm_normal_bg_color > 0)
7877 color = cterm_normal_bg_color - 1;
7878 else
7879 {
7880 EMSG(_("E420: BG color unknown"));
7881 error = TRUE;
7882 break;
7883 }
7884 }
7885 else
7886 {
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007887 int bold = MAYBE;
7888
Bram Moolenaar071d4272004-06-13 20:20:40 +00007889#if defined(__QNXNTO__)
7890 static int *color_numbers_8_qansi = color_numbers_8;
7891 /* On qnx, the 8 & 16 color arrays are the same */
7892 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7893 color_numbers_8_qansi = color_numbers_16;
7894#endif
7895
7896 /* reduce calls to STRICMP a bit, it can be slow */
7897 off = TOUPPER_ASC(*arg);
7898 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7899 if (off == color_names[i][0]
7900 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7901 break;
7902 if (i < 0)
7903 {
7904 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7905 error = TRUE;
7906 break;
7907 }
7908
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007909 color = lookup_color(i, key[5] == 'F', &bold);
7910
7911 /* set/reset bold attribute to get light foreground
7912 * colors (on some terminals, e.g. "linux") */
7913 if (bold == TRUE)
7914 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007915 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7916 HL_TABLE()[idx].sg_cterm_bold = TRUE;
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007917 }
7918 else if (bold == FALSE)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007919 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007920 }
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007921
Bram Moolenaarccbab932010-05-13 15:40:30 +02007922 /* Add one to the argument, to avoid zero. Zero is used for
7923 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007924 if (key[5] == 'F')
7925 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007926 HL_TABLE()[idx].sg_cterm_fg = color + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007927 if (is_normal_group)
7928 {
7929 cterm_normal_fg_color = color + 1;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007930 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007931#ifdef FEAT_GUI
7932 /* Don't do this if the GUI is used. */
7933 if (!gui.in_use && !gui.starting)
7934#endif
7935 {
7936 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007937 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007938 term_fg_color(color);
7939 }
7940 }
7941 }
7942 else
7943 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007944 HL_TABLE()[idx].sg_cterm_bg = color + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007945 if (is_normal_group)
7946 {
7947 cterm_normal_bg_color = color + 1;
7948#ifdef FEAT_GUI
7949 /* Don't mess with 'background' if the GUI is used. */
7950 if (!gui.in_use && !gui.starting)
7951#endif
7952 {
7953 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007954 if (color >= 0)
7955 {
Bram Moolenaar1615b362017-06-04 21:06:09 +02007956 int dark = -1;
7957
Bram Moolenaarccbab932010-05-13 15:40:30 +02007958 if (termcap_active)
7959 term_bg_color(color);
7960 if (t_colors < 16)
Bram Moolenaar1615b362017-06-04 21:06:09 +02007961 dark = (color == 0 || color == 4);
7962 /* Limit the heuristic to the standard 16 colors */
7963 else if (color < 16)
7964 dark = (color < 7 || color == 8);
Bram Moolenaarccbab932010-05-13 15:40:30 +02007965 /* Set the 'background' option if the value is
7966 * wrong. */
Bram Moolenaar1615b362017-06-04 21:06:09 +02007967 if (dark != -1
7968 && dark != (*p_bg == 'd')
7969 && !option_was_set((char_u *)"bg"))
7970 {
Bram Moolenaarccbab932010-05-13 15:40:30 +02007971 set_option_value((char_u *)"bg", 0L,
Bram Moolenaar1615b362017-06-04 21:06:09 +02007972 (char_u *)(dark ? "dark" : "light"), 0);
7973 reset_option_was_set((char_u *)"bg");
7974 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007975 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007976 }
7977 }
7978 }
7979 }
7980 }
7981 else if (STRCMP(key, "GUIFG") == 0)
7982 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02007983#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02007984 char_u **namep = &HL_TABLE()[idx].sg_gui_fg_name;
7985
Bram Moolenaar414168d2017-09-10 15:21:55 +02007986 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007987 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007988 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007989 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007990
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007991# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007992 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007993 i = color_name2handle(arg);
Bram Moolenaar63122562016-04-30 12:28:15 +02007994 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007995 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007996 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007997# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02007998 if (*namep == NULL || STRCMP(*namep, arg) != 0)
7999 {
8000 vim_free(*namep);
8001 if (STRCMP(arg, "NONE") != 0)
8002 *namep = vim_strsave(arg);
8003 else
8004 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008005 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008006 }
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008007# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008008# ifdef FEAT_GUI_X11
Bram Moolenaar452030e2017-09-25 22:57:27 +02008009 if (is_menu_group && gui.menu_fg_pixel != i)
8010 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008011 gui.menu_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008012 do_colors = TRUE;
8013 }
8014 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
8015 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008016 gui.scroll_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008017 do_colors = TRUE;
8018 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008019# ifdef FEAT_BEVAL_GUI
Bram Moolenaar452030e2017-09-25 22:57:27 +02008020 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
8021 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008022 gui.tooltip_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008023 do_colors = TRUE;
8024 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008025# endif
Bram Moolenaar61623362010-07-14 22:04:22 +02008026# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008027 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008028# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008029 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008030#endif
8031 }
8032 else if (STRCMP(key, "GUIBG") == 0)
8033 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02008034#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02008035 char_u **namep = &HL_TABLE()[idx].sg_gui_bg_name;
8036
Bram Moolenaar414168d2017-09-10 15:21:55 +02008037 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008038 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008039 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008040 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008041
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008042# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008043 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008044 i = color_name2handle(arg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008045 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008046 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008047 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008048# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02008049 if (*namep == NULL || STRCMP(*namep, arg) != 0)
8050 {
8051 vim_free(*namep);
8052 if (STRCMP(arg, "NONE") != 0)
8053 *namep = vim_strsave(arg);
8054 else
8055 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008056 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008057 }
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008058# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008059# ifdef FEAT_GUI_X11
Bram Moolenaar452030e2017-09-25 22:57:27 +02008060 if (is_menu_group && gui.menu_bg_pixel != i)
8061 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008062 gui.menu_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008063 do_colors = TRUE;
8064 }
8065 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
8066 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008067 gui.scroll_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008068 do_colors = TRUE;
8069 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008070# ifdef FEAT_BEVAL_GUI
Bram Moolenaar452030e2017-09-25 22:57:27 +02008071 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
8072 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008073 gui.tooltip_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008074 do_colors = TRUE;
8075 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008076# endif
Bram Moolenaar61623362010-07-14 22:04:22 +02008077# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008078 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008079# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008080 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008081#endif
8082 }
8083 else if (STRCMP(key, "GUISP") == 0)
8084 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02008085#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02008086 char_u **namep = &HL_TABLE()[idx].sg_gui_sp_name;
8087
Bram Moolenaar414168d2017-09-10 15:21:55 +02008088 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008089 {
8090 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008091 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008092
Bram Moolenaar61623362010-07-14 22:04:22 +02008093# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008094 i = color_name2handle(arg);
8095 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
8096 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008097 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008098# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02008099 if (*namep == NULL || STRCMP(*namep, arg) != 0)
8100 {
8101 vim_free(*namep);
8102 if (STRCMP(arg, "NONE") != 0)
8103 *namep = vim_strsave(arg);
8104 else
8105 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008106 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008107 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008108# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008109 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008110# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008111 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008112#endif
8113 }
8114 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
8115 {
8116 char_u buf[100];
8117 char_u *tname;
8118
8119 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008120 HL_TABLE()[idx].sg_set |= SG_TERM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008121
8122 /*
8123 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008124 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008125 */
8126 if (STRNCMP(arg, "t_", 2) == 0)
8127 {
8128 off = 0;
8129 buf[0] = 0;
8130 while (arg[off] != NUL)
8131 {
8132 /* Isolate one termcap name */
8133 for (len = 0; arg[off + len] &&
8134 arg[off + len] != ','; ++len)
8135 ;
8136 tname = vim_strnsave(arg + off, len);
8137 if (tname == NULL) /* out of memory */
8138 {
8139 error = TRUE;
8140 break;
8141 }
8142 /* lookup the escape sequence for the item */
8143 p = get_term_code(tname);
8144 vim_free(tname);
8145 if (p == NULL) /* ignore non-existing things */
8146 p = (char_u *)"";
8147
8148 /* Append it to the already found stuff */
8149 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
8150 {
8151 EMSG2(_("E422: terminal code too long: %s"), arg);
8152 error = TRUE;
8153 break;
8154 }
8155 STRCAT(buf, p);
8156
8157 /* Advance to the next item */
8158 off += len;
8159 if (arg[off] == ',') /* another one follows */
8160 ++off;
8161 }
8162 }
8163 else
8164 {
8165 /*
8166 * Copy characters from arg[] to buf[], translating <> codes.
8167 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008168 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00008169 {
Bram Moolenaar35a4cfa2016-08-14 16:07:48 +02008170 len = trans_special(&p, buf + off, FALSE, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008171 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008172 off += len;
8173 else /* copy as normal char */
8174 buf[off++] = *p++;
8175 }
8176 buf[off] = NUL;
8177 }
8178 if (error)
8179 break;
8180
8181 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
8182 p = NULL;
8183 else
8184 p = vim_strsave(buf);
8185 if (key[2] == 'A')
8186 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008187 vim_free(HL_TABLE()[idx].sg_start);
8188 HL_TABLE()[idx].sg_start = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008189 }
8190 else
8191 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008192 vim_free(HL_TABLE()[idx].sg_stop);
8193 HL_TABLE()[idx].sg_stop = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008194 }
8195 }
8196 else
8197 {
8198 EMSG2(_("E423: Illegal argument: %s"), key_start);
8199 error = TRUE;
8200 break;
8201 }
Bram Moolenaar414168d2017-09-10 15:21:55 +02008202 HL_TABLE()[idx].sg_cleared = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008203
8204 /*
8205 * When highlighting has been given for a group, don't link it.
8206 */
Bram Moolenaar414168d2017-09-10 15:21:55 +02008207 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8208 HL_TABLE()[idx].sg_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008209
8210 /*
8211 * Continue with next argument.
8212 */
8213 linep = skipwhite(linep);
8214 }
8215
8216 /*
8217 * If there is an error, and it's a new entry, remove it from the table.
8218 */
8219 if (error && idx == highlight_ga.ga_len)
8220 syn_unadd_group();
8221 else
8222 {
8223 if (is_normal_group)
8224 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008225 HL_TABLE()[idx].sg_term_attr = 0;
8226 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008227#ifdef FEAT_GUI
Bram Moolenaar414168d2017-09-10 15:21:55 +02008228 HL_TABLE()[idx].sg_gui_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008229 /*
8230 * Need to update all groups, because they might be using "bg"
8231 * and/or "fg", which have been changed now.
8232 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008233#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008234#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008235 if (USE_24BIT)
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008236 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008237 highlight_gui_started();
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008238 did_highlight_changed = TRUE;
8239 redraw_all_later(NOT_VALID);
8240 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008241#endif
8242 }
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01008243#ifdef FEAT_TERMINAL
8244 else if (is_terminal_group)
8245 set_terminal_default_colors(
8246 HL_TABLE()[idx].sg_cterm_fg, HL_TABLE()[idx].sg_cterm_bg);
8247#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008248#ifdef FEAT_GUI_X11
8249# ifdef FEAT_MENU
8250 else if (is_menu_group)
8251 {
8252 if (gui.in_use && do_colors)
8253 gui_mch_new_menu_colors();
8254 }
8255# endif
8256 else if (is_scrollbar_group)
8257 {
8258 if (gui.in_use && do_colors)
8259 gui_new_scrollbar_colors();
8260 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008261# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00008262 else if (is_tooltip_group)
8263 {
8264 if (gui.in_use && do_colors)
8265 gui_mch_new_tooltip_colors();
8266 }
8267# endif
8268#endif
8269 else
8270 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008271#ifdef FEAT_EVAL
Bram Moolenaar414168d2017-09-10 15:21:55 +02008272 HL_TABLE()[idx].sg_scriptID = current_SID;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008273#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008274 }
Bram Moolenaar99433292017-09-08 12:37:47 +02008275
Bram Moolenaar071d4272004-06-13 20:20:40 +00008276 vim_free(key);
8277 vim_free(arg);
8278
Bram Moolenaar99433292017-09-08 12:37:47 +02008279 /* Only call highlight_changed() once, after a sequence of highlight
8280 * commands, and only if an attribute actually changed. */
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008281 if ((did_change
8282 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008283#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
8284 && !did_highlight_changed
8285#endif
8286 )
Bram Moolenaar99433292017-09-08 12:37:47 +02008287 {
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008288 /* Do not trigger a redraw when highlighting is changed while
8289 * redrawing. This may happen when evaluating 'statusline' changes the
8290 * StatusLine group. */
8291 if (!updating_screen)
8292 redraw_all_later(NOT_VALID);
Bram Moolenaar99433292017-09-08 12:37:47 +02008293 need_highlight_changed = TRUE;
8294 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008295}
8296
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008297#if defined(EXITFREE) || defined(PROTO)
8298 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008299free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008300{
8301 int i;
8302
8303 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008304 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008305 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008306 vim_free(HL_TABLE()[i].sg_name);
8307 vim_free(HL_TABLE()[i].sg_name_u);
8308 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008309 ga_clear(&highlight_ga);
8310}
8311#endif
8312
Bram Moolenaar071d4272004-06-13 20:20:40 +00008313/*
8314 * Reset the cterm colors to what they were before Vim was started, if
8315 * possible. Otherwise reset them to zero.
8316 */
8317 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008318restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008319{
Bram Moolenaar48e330a2016-02-23 14:53:34 +01008320#if defined(WIN3264) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008321 /* Since t_me has been set, this probably means that the user
8322 * wants to use this as default colors. Need to reset default
8323 * background/foreground colors. */
8324 mch_set_normal_colors();
8325#else
8326 cterm_normal_fg_color = 0;
8327 cterm_normal_fg_bold = 0;
8328 cterm_normal_bg_color = 0;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008329# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008330 cterm_normal_fg_gui_color = INVALCOLOR;
8331 cterm_normal_bg_gui_color = INVALCOLOR;
8332# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008333#endif
8334}
8335
8336/*
8337 * Return TRUE if highlight group "idx" has any settings.
8338 * When "check_link" is TRUE also check for an existing link.
8339 */
8340 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008341hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008342{
8343 return ( HL_TABLE()[idx].sg_term_attr != 0
8344 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008345 || HL_TABLE()[idx].sg_cterm_fg != 0
8346 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008347#ifdef FEAT_GUI
8348 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008349 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8350 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8351 || HL_TABLE()[idx].sg_gui_sp_name != NULL
Bram Moolenaar87748452017-03-12 17:10:33 +01008352 || HL_TABLE()[idx].sg_font_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008353#endif
8354 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8355}
8356
8357/*
8358 * Clear highlighting for one group.
8359 */
8360 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008361highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008362{
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008363 HL_TABLE()[idx].sg_cleared = TRUE;
8364
Bram Moolenaar071d4272004-06-13 20:20:40 +00008365 HL_TABLE()[idx].sg_term = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +01008366 VIM_CLEAR(HL_TABLE()[idx].sg_start);
8367 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008368 HL_TABLE()[idx].sg_term_attr = 0;
8369 HL_TABLE()[idx].sg_cterm = 0;
8370 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8371 HL_TABLE()[idx].sg_cterm_fg = 0;
8372 HL_TABLE()[idx].sg_cterm_bg = 0;
8373 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008374#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008375 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +01008376 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
8377 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
8378 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
Bram Moolenaar61623362010-07-14 22:04:22 +02008379#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008380#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008381 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8382 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008383#endif
8384#ifdef FEAT_GUI
Bram Moolenaar61623362010-07-14 22:04:22 +02008385 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008386 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8387 HL_TABLE()[idx].sg_font = NOFONT;
8388# ifdef FEAT_XFONTSET
8389 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8390 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8391# endif
Bram Moolenaard23a8232018-02-10 18:45:26 +01008392 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008393 HL_TABLE()[idx].sg_gui_attr = 0;
8394#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008395#ifdef FEAT_EVAL
8396 /* Clear the script ID only when there is no link, since that is not
8397 * cleared. */
8398 if (HL_TABLE()[idx].sg_link == 0)
8399 HL_TABLE()[idx].sg_scriptID = 0;
8400#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008401}
8402
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008403#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008404/*
8405 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008406 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008407 * "Tooltip" colors.
8408 */
8409 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008410set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008411{
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008412#ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008413# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008414 if (gui.in_use)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008415# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008416 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008417 if (set_group_colors((char_u *)"Normal",
8418 &gui.norm_pixel, &gui.back_pixel,
8419 FALSE, TRUE, FALSE))
8420 {
8421 gui_mch_new_colors();
8422 must_redraw = CLEAR;
8423 }
8424# ifdef FEAT_GUI_X11
8425 if (set_group_colors((char_u *)"Menu",
8426 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8427 TRUE, FALSE, FALSE))
8428 {
8429# ifdef FEAT_MENU
8430 gui_mch_new_menu_colors();
8431# endif
8432 must_redraw = CLEAR;
8433 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008434# ifdef FEAT_BEVAL_GUI
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008435 if (set_group_colors((char_u *)"Tooltip",
8436 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8437 FALSE, FALSE, TRUE))
8438 {
8439# ifdef FEAT_TOOLBAR
8440 gui_mch_new_tooltip_colors();
8441# endif
8442 must_redraw = CLEAR;
8443 }
8444# endif
8445 if (set_group_colors((char_u *)"Scrollbar",
8446 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8447 FALSE, FALSE, FALSE))
8448 {
8449 gui_new_scrollbar_colors();
8450 must_redraw = CLEAR;
8451 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008452# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008453 }
8454#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008455#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008456# ifdef FEAT_GUI
8457 else
8458# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008459 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008460 int idx;
8461
8462 idx = syn_name2id((char_u *)"Normal") - 1;
8463 if (idx >= 0)
8464 {
8465 gui_do_one_color(idx, FALSE, FALSE);
8466
8467 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8468 {
8469 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
8470 must_redraw = CLEAR;
8471 }
8472 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8473 {
8474 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
8475 must_redraw = CLEAR;
8476 }
8477 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008478 }
8479#endif
8480}
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008481#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008482
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008483#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008484/*
8485 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8486 */
8487 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008488set_group_colors(
8489 char_u *name,
8490 guicolor_T *fgp,
8491 guicolor_T *bgp,
8492 int do_menu,
8493 int use_norm,
8494 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008495{
8496 int idx;
8497
8498 idx = syn_name2id(name) - 1;
8499 if (idx >= 0)
8500 {
8501 gui_do_one_color(idx, do_menu, do_tooltip);
8502
8503 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8504 *fgp = HL_TABLE()[idx].sg_gui_fg;
8505 else if (use_norm)
8506 *fgp = gui.def_norm_pixel;
8507 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8508 *bgp = HL_TABLE()[idx].sg_gui_bg;
8509 else if (use_norm)
8510 *bgp = gui.def_back_pixel;
8511 return TRUE;
8512 }
8513 return FALSE;
8514}
8515
8516/*
8517 * Get the font of the "Normal" group.
8518 * Returns "" when it's not found or not set.
8519 */
8520 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008521hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008522{
8523 int id;
8524 char_u *s;
8525
8526 id = syn_name2id((char_u *)"Normal");
8527 if (id > 0)
8528 {
8529 s = HL_TABLE()[id - 1].sg_font_name;
8530 if (s != NULL)
8531 return s;
8532 }
8533 return (char_u *)"";
8534}
8535
8536/*
8537 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8538 * actually chosen to be used.
8539 */
8540 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008541hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008542{
8543 int id;
8544
8545 id = syn_name2id((char_u *)"Normal");
8546 if (id > 0)
8547 {
8548 vim_free(HL_TABLE()[id - 1].sg_font_name);
8549 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8550 }
8551}
8552
8553/*
8554 * Set background color for "Normal" group. Called by gui_set_bg_color()
8555 * when the color is known.
8556 */
8557 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008558hl_set_bg_color_name(
8559 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008560{
8561 int id;
8562
8563 if (name != NULL)
8564 {
8565 id = syn_name2id((char_u *)"Normal");
8566 if (id > 0)
8567 {
8568 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8569 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8570 }
8571 }
8572}
8573
8574/*
8575 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8576 * when the color is known.
8577 */
8578 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008579hl_set_fg_color_name(
8580 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008581{
8582 int id;
8583
8584 if (name != NULL)
8585 {
8586 id = syn_name2id((char_u *)"Normal");
8587 if (id > 0)
8588 {
8589 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8590 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8591 }
8592 }
8593}
8594
8595/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008596 * Return the handle for a font name.
8597 * Returns NOFONT when failed.
8598 */
8599 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008600font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008601{
8602 if (STRCMP(name, "NONE") == 0)
8603 return NOFONT;
8604
8605 return gui_mch_get_font(name, TRUE);
8606}
8607
8608# ifdef FEAT_XFONTSET
8609/*
8610 * Return the handle for a fontset name.
8611 * Returns NOFONTSET when failed.
8612 */
8613 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008614fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008615{
8616 if (STRCMP(name, "NONE") == 0)
8617 return NOFONTSET;
8618
8619 return gui_mch_get_fontset(name, TRUE, fixed_width);
8620}
8621# endif
8622
8623/*
8624 * Get the font or fontset for one highlight group.
8625 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008626 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008627hl_do_font(
8628 int idx,
8629 char_u *arg,
8630 int do_normal, /* set normal font */
8631 int do_menu UNUSED, /* set menu font */
8632 int do_tooltip UNUSED, /* set tooltip font */
8633 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008634{
8635# ifdef FEAT_XFONTSET
8636 /* If 'guifontset' is not empty, first try using the name as a
8637 * fontset. If that doesn't work, use it as a font name. */
8638 if (*p_guifontset != NUL
8639# ifdef FONTSET_ALWAYS
8640 || do_menu
8641# endif
8642# ifdef FEAT_BEVAL_TIP
8643 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8644 || do_tooltip
8645# endif
8646 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008647 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008648 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008649 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008650 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8651# ifdef FONTSET_ALWAYS
8652 || do_menu
8653# endif
8654# ifdef FEAT_BEVAL_TIP
8655 || do_tooltip
8656# endif
8657 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008658 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008659 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8660 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008661 /* If it worked and it's the Normal group, use it as the normal
8662 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008663 if (do_normal)
8664 gui_init_font(arg, TRUE);
8665# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8666 if (do_menu)
8667 {
8668# ifdef FONTSET_ALWAYS
8669 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8670# else
8671 /* YIKES! This is a bug waiting to crash the program */
8672 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8673# endif
8674 gui_mch_new_menu_font();
8675 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008676# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00008677 if (do_tooltip)
8678 {
8679 /* The Athena widget set cannot currently handle switching between
8680 * displaying a single font and a fontset.
8681 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008682 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008683 * XFontStruct is used.
8684 */
8685 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8686 gui_mch_new_tooltip_font();
8687 }
8688# endif
8689# endif
8690 }
8691 else
8692# endif
8693 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008694 if (free_font)
8695 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008696 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8697 /* If it worked and it's the Normal group, use it as the
8698 * normal font. Same for the Menu group. */
8699 if (HL_TABLE()[idx].sg_font != NOFONT)
8700 {
8701 if (do_normal)
8702 gui_init_font(arg, FALSE);
8703#ifndef FONTSET_ALWAYS
8704# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8705 if (do_menu)
8706 {
8707 gui.menu_font = HL_TABLE()[idx].sg_font;
8708 gui_mch_new_menu_font();
8709 }
8710# endif
8711#endif
8712 }
8713 }
8714}
8715
8716#endif /* FEAT_GUI */
8717
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008718#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008719/*
8720 * Return the handle for a color name.
8721 * Returns INVALCOLOR when failed.
8722 */
Bram Moolenaar3d9bdfe2017-08-12 22:55:58 +02008723 guicolor_T
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008724color_name2handle(char_u *name)
8725{
8726 if (STRCMP(name, "NONE") == 0)
8727 return INVALCOLOR;
8728
8729 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8730 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008731#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008732 if (gui.in_use)
8733#endif
8734#ifdef FEAT_GUI
8735 return gui.norm_pixel;
8736#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008737#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008738 if (cterm_normal_fg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008739 return cterm_normal_fg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008740 /* Guess that the foreground is black or white. */
8741 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008742#endif
8743 }
8744 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8745 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008746#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008747 if (gui.in_use)
8748#endif
8749#ifdef FEAT_GUI
8750 return gui.back_pixel;
8751#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008752#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008753 if (cterm_normal_bg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008754 return cterm_normal_bg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008755 /* Guess that the background is white or black. */
8756 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008757#endif
8758 }
8759
8760 return GUI_GET_COLOR(name);
8761}
8762#endif
8763
Bram Moolenaar071d4272004-06-13 20:20:40 +00008764/*
8765 * Table with the specifications for an attribute number.
8766 * Note that this table is used by ALL buffers. This is required because the
8767 * GUI can redraw at any time for any buffer.
8768 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008769static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008770
8771#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8772
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008773static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008774
8775#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8776
8777#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008778static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008779
8780#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8781#endif
8782
8783/*
8784 * Return the attr number for a set of colors and font.
8785 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8786 * if the combination is new.
8787 * Return 0 for error (no more room).
8788 */
8789 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008790get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008791{
8792 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008793 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008794 static int recursive = FALSE;
8795
8796 /*
8797 * Init the table, in case it wasn't done yet.
8798 */
8799 table->ga_itemsize = sizeof(attrentry_T);
8800 table->ga_growsize = 7;
8801
8802 /*
8803 * Try to find an entry with the same specifications.
8804 */
8805 for (i = 0; i < table->ga_len; ++i)
8806 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008807 taep = &(((attrentry_T *)table->ga_data)[i]);
8808 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008809 && (
8810#ifdef FEAT_GUI
8811 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008812 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8813 && aep->ae_u.gui.bg_color
8814 == taep->ae_u.gui.bg_color
8815 && aep->ae_u.gui.sp_color
8816 == taep->ae_u.gui.sp_color
8817 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008818# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008819 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008820# endif
8821 ))
8822 ||
8823#endif
8824 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008825 && (aep->ae_u.term.start == NULL)
8826 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008827 && (aep->ae_u.term.start == NULL
8828 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008829 taep->ae_u.term.start) == 0)
8830 && (aep->ae_u.term.stop == NULL)
8831 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008832 && (aep->ae_u.term.stop == NULL
8833 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008834 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008835 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008836 && aep->ae_u.cterm.fg_color
8837 == taep->ae_u.cterm.fg_color
8838 && aep->ae_u.cterm.bg_color
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008839 == taep->ae_u.cterm.bg_color
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008840#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008841 && aep->ae_u.cterm.fg_rgb
8842 == taep->ae_u.cterm.fg_rgb
8843 && aep->ae_u.cterm.bg_rgb
8844 == taep->ae_u.cterm.bg_rgb
8845#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008846 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008847
8848 return i + ATTR_OFF;
8849 }
8850
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008851 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008852 {
8853 /*
8854 * Running out of attribute entries! remove all attributes, and
8855 * compute new ones for all groups.
8856 * When called recursively, we are really out of numbers.
8857 */
8858 if (recursive)
8859 {
8860 EMSG(_("E424: Too many different highlighting attributes in use"));
8861 return 0;
8862 }
8863 recursive = TRUE;
8864
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008865 clear_hl_tables();
8866
Bram Moolenaar071d4272004-06-13 20:20:40 +00008867 must_redraw = CLEAR;
8868
8869 for (i = 0; i < highlight_ga.ga_len; ++i)
8870 set_hl_attr(i);
8871
8872 recursive = FALSE;
8873 }
8874
8875 /*
8876 * This is a new combination of colors and font, add an entry.
8877 */
8878 if (ga_grow(table, 1) == FAIL)
8879 return 0;
8880
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008881 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8882 vim_memset(taep, 0, sizeof(attrentry_T));
8883 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008884#ifdef FEAT_GUI
8885 if (table == &gui_attr_table)
8886 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008887 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8888 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8889 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8890 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008891# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008892 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008893# endif
8894 }
8895#endif
8896 if (table == &term_attr_table)
8897 {
8898 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008899 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008900 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008901 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008902 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008903 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008904 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008905 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008906 }
8907 else if (table == &cterm_attr_table)
8908 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008909 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8910 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008911#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008912 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
8913 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
8914#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008915 }
8916 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008917 return (table->ga_len - 1 + ATTR_OFF);
8918}
8919
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008920/*
Bram Moolenaareeac6772017-07-23 15:48:37 +02008921 * Get an attribute index for a cterm entry.
8922 * Uses an existing entry when possible or adds one when needed.
8923 */
8924 int
8925get_cterm_attr_idx(int attr, int fg, int bg)
8926{
8927 attrentry_T at_en;
8928
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008929 vim_memset(&at_en, 0, sizeof(attrentry_T));
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008930#ifdef FEAT_TERMGUICOLORS
8931 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
8932 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
8933#endif
Bram Moolenaareeac6772017-07-23 15:48:37 +02008934 at_en.ae_attr = attr;
8935 at_en.ae_u.cterm.fg_color = fg;
8936 at_en.ae_u.cterm.bg_color = bg;
8937 return get_attr_entry(&cterm_attr_table, &at_en);
8938}
8939
Bram Moolenaar065f41c2017-07-23 18:07:56 +02008940#if defined(FEAT_TERMGUICOLORS) || defined(PROTO)
8941/*
8942 * Get an attribute index for a 'termguicolors' entry.
8943 * Uses an existing entry when possible or adds one when needed.
8944 */
8945 int
8946get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8947{
8948 attrentry_T at_en;
8949
8950 vim_memset(&at_en, 0, sizeof(attrentry_T));
8951 at_en.ae_attr = attr;
8952 at_en.ae_u.cterm.fg_rgb = fg;
8953 at_en.ae_u.cterm.bg_rgb = bg;
8954 return get_attr_entry(&cterm_attr_table, &at_en);
8955}
8956#endif
8957
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008958#if defined(FEAT_GUI) || defined(PROTO)
8959/*
8960 * Get an attribute index for a cterm entry.
8961 * Uses an existing entry when possible or adds one when needed.
8962 */
8963 int
8964get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8965{
8966 attrentry_T at_en;
8967
8968 vim_memset(&at_en, 0, sizeof(attrentry_T));
8969 at_en.ae_attr = attr;
8970 at_en.ae_u.gui.fg_color = fg;
8971 at_en.ae_u.gui.bg_color = bg;
8972 return get_attr_entry(&gui_attr_table, &at_en);
8973}
8974#endif
8975
Bram Moolenaareeac6772017-07-23 15:48:37 +02008976/*
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008977 * Clear all highlight tables.
8978 */
8979 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008980clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008981{
8982 int i;
8983 attrentry_T *taep;
8984
8985#ifdef FEAT_GUI
8986 ga_clear(&gui_attr_table);
8987#endif
8988 for (i = 0; i < term_attr_table.ga_len; ++i)
8989 {
8990 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8991 vim_free(taep->ae_u.term.start);
8992 vim_free(taep->ae_u.term.stop);
8993 }
8994 ga_clear(&term_attr_table);
8995 ga_clear(&cterm_attr_table);
8996}
8997
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008998#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008999/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00009000 * Combine special attributes (e.g., for spelling) with other attributes
9001 * (e.g., for syntax highlighting).
9002 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00009003 * This creates a new group when required.
9004 * Since we expect there to be few spelling mistakes we don't cache the
9005 * result.
9006 * Return the resulting attributes.
9007 */
9008 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009009hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00009010{
9011 attrentry_T *char_aep = NULL;
9012 attrentry_T *spell_aep;
9013 attrentry_T new_en;
9014
9015 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00009016 return prim_attr;
9017 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009018 return ATTR_COMBINE(char_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009019#ifdef FEAT_GUI
9020 if (gui.in_use)
9021 {
9022 if (char_attr > HL_ALL)
9023 char_aep = syn_gui_attr2entry(char_attr);
9024 if (char_aep != NULL)
9025 new_en = *char_aep;
9026 else
9027 {
9028 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00009029 new_en.ae_u.gui.fg_color = INVALCOLOR;
9030 new_en.ae_u.gui.bg_color = INVALCOLOR;
9031 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00009032 if (char_attr <= HL_ALL)
9033 new_en.ae_attr = char_attr;
9034 }
9035
Bram Moolenaar30abd282005-06-22 22:35:10 +00009036 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009037 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009038 else
9039 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00009040 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009041 if (spell_aep != NULL)
9042 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009043 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
9044 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009045 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
9046 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
9047 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
9048 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
9049 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
9050 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
9051 if (spell_aep->ae_u.gui.font != NOFONT)
9052 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
9053# ifdef FEAT_XFONTSET
9054 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
9055 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
9056# endif
9057 }
9058 }
9059 return get_attr_entry(&gui_attr_table, &new_en);
9060 }
9061#endif
9062
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009063 if (IS_CTERM)
Bram Moolenaar217ad922005-03-20 22:37:15 +00009064 {
9065 if (char_attr > HL_ALL)
9066 char_aep = syn_cterm_attr2entry(char_attr);
9067 if (char_aep != NULL)
9068 new_en = *char_aep;
9069 else
9070 {
9071 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaar0cdb72a2017-01-02 21:37:40 +01009072#ifdef FEAT_TERMGUICOLORS
9073 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
9074 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
9075#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00009076 if (char_attr <= HL_ALL)
9077 new_en.ae_attr = char_attr;
9078 }
9079
Bram Moolenaar30abd282005-06-22 22:35:10 +00009080 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009081 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009082 else
9083 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00009084 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009085 if (spell_aep != NULL)
9086 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009087 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
9088 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009089 if (spell_aep->ae_u.cterm.fg_color > 0)
9090 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
9091 if (spell_aep->ae_u.cterm.bg_color > 0)
9092 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009093#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009094 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009095 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009096 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009097 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
9098#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00009099 }
9100 }
9101 return get_attr_entry(&cterm_attr_table, &new_en);
9102 }
9103
9104 if (char_attr > HL_ALL)
9105 char_aep = syn_term_attr2entry(char_attr);
9106 if (char_aep != NULL)
9107 new_en = *char_aep;
9108 else
9109 {
9110 vim_memset(&new_en, 0, sizeof(new_en));
9111 if (char_attr <= HL_ALL)
9112 new_en.ae_attr = char_attr;
9113 }
9114
Bram Moolenaar30abd282005-06-22 22:35:10 +00009115 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009116 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009117 else
9118 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00009119 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009120 if (spell_aep != NULL)
9121 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009122 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009123 if (spell_aep->ae_u.term.start != NULL)
9124 {
9125 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
9126 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
9127 }
9128 }
9129 }
9130 return get_attr_entry(&term_attr_table, &new_en);
9131}
9132#endif
9133
Bram Moolenaar071d4272004-06-13 20:20:40 +00009134#ifdef FEAT_GUI
9135
9136 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009137syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009138{
9139 attr -= ATTR_OFF;
9140 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
9141 return NULL;
9142 return &(GUI_ATTR_ENTRY(attr));
9143}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009144#endif /* FEAT_GUI */
9145
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009146/*
9147 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
9148 * Only to be used when "attr" > HL_ALL.
9149 */
9150 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009151syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009152{
9153 attrentry_T *aep;
9154
9155#ifdef FEAT_GUI
9156 if (gui.in_use)
9157 aep = syn_gui_attr2entry(attr);
9158 else
9159#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009160 if (IS_CTERM)
9161 aep = syn_cterm_attr2entry(attr);
9162 else
9163 aep = syn_term_attr2entry(attr);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009164
9165 if (aep == NULL) /* highlighting not set */
9166 return 0;
9167 return aep->ae_attr;
9168}
9169
9170
Bram Moolenaar071d4272004-06-13 20:20:40 +00009171 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009172syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009173{
9174 attr -= ATTR_OFF;
9175 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
9176 return NULL;
9177 return &(TERM_ATTR_ENTRY(attr));
9178}
9179
9180 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009181syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009182{
9183 attr -= ATTR_OFF;
9184 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
9185 return NULL;
9186 return &(CTERM_ATTR_ENTRY(attr));
9187}
9188
9189#define LIST_ATTR 1
9190#define LIST_STRING 2
9191#define LIST_INT 3
9192
9193 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009194highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009195{
9196 struct hl_group *sgp;
9197 int didh = FALSE;
9198
9199 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
9200
9201 didh = highlight_list_arg(id, didh, LIST_ATTR,
9202 sgp->sg_term, NULL, "term");
9203 didh = highlight_list_arg(id, didh, LIST_STRING,
9204 0, sgp->sg_start, "start");
9205 didh = highlight_list_arg(id, didh, LIST_STRING,
9206 0, sgp->sg_stop, "stop");
9207
9208 didh = highlight_list_arg(id, didh, LIST_ATTR,
9209 sgp->sg_cterm, NULL, "cterm");
9210 didh = highlight_list_arg(id, didh, LIST_INT,
9211 sgp->sg_cterm_fg, NULL, "ctermfg");
9212 didh = highlight_list_arg(id, didh, LIST_INT,
9213 sgp->sg_cterm_bg, NULL, "ctermbg");
9214
Bram Moolenaar61623362010-07-14 22:04:22 +02009215#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009216 didh = highlight_list_arg(id, didh, LIST_ATTR,
9217 sgp->sg_gui, NULL, "gui");
9218 didh = highlight_list_arg(id, didh, LIST_STRING,
9219 0, sgp->sg_gui_fg_name, "guifg");
9220 didh = highlight_list_arg(id, didh, LIST_STRING,
9221 0, sgp->sg_gui_bg_name, "guibg");
9222 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009223 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02009224#endif
9225#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009226 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00009227 0, sgp->sg_font_name, "font");
9228#endif
9229
Bram Moolenaar661b1822005-07-28 22:36:45 +00009230 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009231 {
9232 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00009233 didh = TRUE;
Bram Moolenaar8820b482017-03-16 17:23:31 +01009234 msg_puts_attr((char_u *)"links to", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009235 msg_putchar(' ');
9236 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
9237 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009238
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009239 if (!didh)
9240 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00009241#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009242 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009243 last_set_msg(sgp->sg_scriptID);
9244#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009245}
9246
9247 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009248highlight_list_arg(
9249 int id,
9250 int didh,
9251 int type,
9252 int iarg,
9253 char_u *sarg,
9254 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009255{
9256 char_u buf[100];
9257 char_u *ts;
9258 int i;
9259
Bram Moolenaar661b1822005-07-28 22:36:45 +00009260 if (got_int)
9261 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009262 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
9263 {
9264 ts = buf;
9265 if (type == LIST_INT)
9266 sprintf((char *)buf, "%d", iarg - 1);
9267 else if (type == LIST_STRING)
9268 ts = sarg;
9269 else /* type == LIST_ATTR */
9270 {
9271 buf[0] = NUL;
9272 for (i = 0; hl_attr_table[i] != 0; ++i)
9273 {
9274 if (iarg & hl_attr_table[i])
9275 {
9276 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02009277 vim_strcat(buf, (char_u *)",", 100);
9278 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009279 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
9280 }
9281 }
9282 }
9283
9284 (void)syn_list_header(didh,
9285 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9286 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00009287 if (!got_int)
9288 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009289 if (*name != NUL)
9290 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01009291 MSG_PUTS_ATTR(name, HL_ATTR(HLF_D));
9292 MSG_PUTS_ATTR("=", HL_ATTR(HLF_D));
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009293 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009294 msg_outtrans(ts);
9295 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009296 }
9297 return didh;
9298}
9299
9300#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9301/*
9302 * Return "1" if highlight group "id" has attribute "flag".
9303 * Return NULL otherwise.
9304 */
9305 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009306highlight_has_attr(
9307 int id,
9308 int flag,
9309 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009310{
9311 int attr;
9312
9313 if (id <= 0 || id > highlight_ga.ga_len)
9314 return NULL;
9315
Bram Moolenaar61623362010-07-14 22:04:22 +02009316#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009317 if (modec == 'g')
9318 attr = HL_TABLE()[id - 1].sg_gui;
9319 else
9320#endif
9321 if (modec == 'c')
9322 attr = HL_TABLE()[id - 1].sg_cterm;
9323 else
9324 attr = HL_TABLE()[id - 1].sg_term;
9325
9326 if (attr & flag)
9327 return (char_u *)"1";
9328 return NULL;
9329}
9330#endif
9331
9332#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9333/*
9334 * Return color name of highlight group "id".
9335 */
9336 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009337highlight_color(
9338 int id,
9339 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
9340 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009341{
9342 static char_u name[20];
9343 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009344 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009345 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009346 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009347
9348 if (id <= 0 || id > highlight_ga.ga_len)
9349 return NULL;
9350
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009351 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009352 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009353 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02009354 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009355 font = TRUE;
9356 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009357 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009358 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9359 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009360 if (modec == 'g')
9361 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009362# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009363# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009364 /* return font name */
9365 if (font)
9366 return HL_TABLE()[id - 1].sg_font_name;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009367# endif
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009368
Bram Moolenaar071d4272004-06-13 20:20:40 +00009369 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009370 if ((USE_24BIT) && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009371 {
9372 guicolor_T color;
9373 long_u rgb;
9374 static char_u buf[10];
9375
9376 if (fg)
9377 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009378 else if (sp)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009379# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009380 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009381# else
9382 color = INVALCOLOR;
9383# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009384 else
9385 color = HL_TABLE()[id - 1].sg_gui_bg;
9386 if (color == INVALCOLOR)
9387 return NULL;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009388 rgb = (long_u)GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009389 sprintf((char *)buf, "#%02x%02x%02x",
9390 (unsigned)(rgb >> 16),
9391 (unsigned)(rgb >> 8) & 255,
9392 (unsigned)rgb & 255);
9393 return buf;
9394 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009395# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009396 if (fg)
9397 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009398 if (sp)
9399 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009400 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9401 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009402 if (font || sp)
9403 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009404 if (modec == 'c')
9405 {
9406 if (fg)
9407 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9408 else
9409 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009410 if (n < 0)
9411 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009412 sprintf((char *)name, "%d", n);
9413 return name;
9414 }
9415 /* term doesn't have color */
9416 return NULL;
9417}
9418#endif
9419
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009420#if (defined(FEAT_SYN_HL) \
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009421 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009422 && defined(FEAT_PRINTER)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009423/*
9424 * Return color name of highlight group "id" as RGB value.
9425 */
9426 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009427highlight_gui_color_rgb(
9428 int id,
9429 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009430{
9431 guicolor_T color;
9432
9433 if (id <= 0 || id > highlight_ga.ga_len)
9434 return 0L;
9435
9436 if (fg)
9437 color = HL_TABLE()[id - 1].sg_gui_fg;
9438 else
9439 color = HL_TABLE()[id - 1].sg_gui_bg;
9440
9441 if (color == INVALCOLOR)
9442 return 0L;
9443
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009444 return GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009445}
9446#endif
9447
9448/*
9449 * Output the syntax list header.
9450 * Return TRUE when started a new line.
9451 */
9452 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009453syn_list_header(
9454 int did_header, /* did header already */
9455 int outlen, /* length of string that comes */
9456 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009457{
9458 int endcol = 19;
9459 int newline = TRUE;
9460
9461 if (!did_header)
9462 {
9463 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009464 if (got_int)
9465 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009466 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9467 endcol = 15;
9468 }
9469 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009470 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009471 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009472 if (got_int)
9473 return TRUE;
9474 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009475 else
9476 {
9477 if (msg_col >= endcol) /* wrap around is like starting a new line */
9478 newline = FALSE;
9479 }
9480
9481 if (msg_col >= endcol) /* output at least one space */
9482 endcol = msg_col + 1;
9483 if (Columns <= endcol) /* avoid hang for tiny window */
9484 endcol = Columns - 1;
9485
9486 msg_advance(endcol);
9487
9488 /* Show "xxx" with the attributes. */
9489 if (!did_header)
9490 {
9491 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9492 msg_putchar(' ');
9493 }
9494
9495 return newline;
9496}
9497
9498/*
9499 * Set the attribute numbers for a highlight group.
9500 * Called after one of the attributes has changed.
9501 */
9502 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009503set_hl_attr(
9504 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009505{
9506 attrentry_T at_en;
9507 struct hl_group *sgp = HL_TABLE() + idx;
9508
9509 /* The "Normal" group doesn't need an attribute number */
9510 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9511 return;
9512
9513#ifdef FEAT_GUI
9514 /*
9515 * For the GUI mode: If there are other than "normal" highlighting
9516 * attributes, need to allocate an attr number.
9517 */
9518 if (sgp->sg_gui_fg == INVALCOLOR
9519 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009520 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009521 && sgp->sg_font == NOFONT
9522# ifdef FEAT_XFONTSET
9523 && sgp->sg_fontset == NOFONTSET
9524# endif
9525 )
9526 {
9527 sgp->sg_gui_attr = sgp->sg_gui;
9528 }
9529 else
9530 {
9531 at_en.ae_attr = sgp->sg_gui;
9532 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9533 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009534 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009535 at_en.ae_u.gui.font = sgp->sg_font;
9536# ifdef FEAT_XFONTSET
9537 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9538# endif
9539 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9540 }
9541#endif
9542 /*
9543 * For the term mode: If there are other than "normal" highlighting
9544 * attributes, need to allocate an attr number.
9545 */
9546 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9547 sgp->sg_term_attr = sgp->sg_term;
9548 else
9549 {
9550 at_en.ae_attr = sgp->sg_term;
9551 at_en.ae_u.term.start = sgp->sg_start;
9552 at_en.ae_u.term.stop = sgp->sg_stop;
9553 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9554 }
9555
9556 /*
9557 * For the color term mode: If there are other than "normal"
9558 * highlighting attributes, need to allocate an attr number.
9559 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009560 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009561# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009562 && sgp->sg_gui_fg == INVALCOLOR
9563 && sgp->sg_gui_bg == INVALCOLOR
9564# endif
9565 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00009566 sgp->sg_cterm_attr = sgp->sg_cterm;
9567 else
9568 {
9569 at_en.ae_attr = sgp->sg_cterm;
9570 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9571 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009572# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarcafafb32018-02-22 21:07:09 +01009573# ifdef WIN3264
9574 {
9575 int id;
9576 guicolor_T fg, bg;
9577
9578 id = syn_name2id((char_u *)"Normal");
9579 if (id > 0)
9580 {
9581 syn_id2colors(id, &fg, &bg);
9582 if (sgp->sg_gui_fg == INVALCOLOR)
9583 sgp->sg_gui_fg = fg;
9584 if (sgp->sg_gui_bg == INVALCOLOR)
9585 sgp->sg_gui_bg = bg;
9586 }
9587
9588 }
9589# endif
Bram Moolenaar187147a2016-05-01 13:09:57 +02009590 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
9591 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009592# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009593 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9594 }
9595}
9596
9597/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009598 * Lookup a highlight group name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009599 * If it is not found, 0 is returned.
9600 */
9601 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009602syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009603{
9604 int i;
9605 char_u name_u[200];
9606
9607 /* Avoid using stricmp() too much, it's slow on some systems */
9608 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9609 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009610 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009611 vim_strup(name_u);
9612 for (i = highlight_ga.ga_len; --i >= 0; )
9613 if (HL_TABLE()[i].sg_name_u != NULL
9614 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9615 break;
9616 return i + 1;
9617}
9618
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02009619/*
9620 * Lookup a highlight group name and return its attributes.
9621 * Return zero if not found.
9622 */
9623 int
9624syn_name2attr(char_u *name)
9625{
9626 int id = syn_name2id(name);
9627
9628 if (id != 0)
Bram Moolenaar76301952017-09-22 13:53:37 +02009629 return syn_id2attr(id);
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02009630 return 0;
9631}
9632
Bram Moolenaar071d4272004-06-13 20:20:40 +00009633#if defined(FEAT_EVAL) || defined(PROTO)
9634/*
9635 * Return TRUE if highlight group "name" exists.
9636 */
9637 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009638highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009639{
9640 return (syn_name2id(name) > 0);
9641}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009642
9643# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9644/*
9645 * Return the name of highlight group "id".
9646 * When not a valid ID return an empty string.
9647 */
9648 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009649syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009650{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009651 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009652 return (char_u *)"";
9653 return HL_TABLE()[id - 1].sg_name;
9654}
9655# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009656#endif
9657
9658/*
9659 * Like syn_name2id(), but take a pointer + length argument.
9660 */
9661 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009662syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009663{
9664 char_u *name;
9665 int id = 0;
9666
9667 name = vim_strnsave(linep, len);
9668 if (name != NULL)
9669 {
9670 id = syn_name2id(name);
9671 vim_free(name);
9672 }
9673 return id;
9674}
9675
9676/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009677 * Find highlight group name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009678 * The argument is a pointer to the name and the length of the name.
9679 * If it doesn't exist yet, a new entry is created.
9680 * Return 0 for failure.
9681 */
9682 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009683syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009684{
9685 int id;
9686 char_u *name;
9687
9688 name = vim_strnsave(pp, len);
9689 if (name == NULL)
9690 return 0;
9691
9692 id = syn_name2id(name);
9693 if (id == 0) /* doesn't exist yet */
9694 id = syn_add_group(name);
9695 else
9696 vim_free(name);
9697 return id;
9698}
9699
9700/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009701 * Add new highlight group and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009702 * "name" must be an allocated string, it will be consumed.
9703 * Return 0 for failure.
9704 */
9705 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009706syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009707{
9708 char_u *p;
9709
9710 /* Check that the name is ASCII letters, digits and underscore. */
9711 for (p = name; *p != NUL; ++p)
9712 {
9713 if (!vim_isprintc(*p))
9714 {
9715 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009716 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009717 return 0;
9718 }
9719 else if (!ASCII_ISALNUM(*p) && *p != '_')
9720 {
9721 /* This is an error, but since there previously was no check only
9722 * give a warning. */
Bram Moolenaar8820b482017-03-16 17:23:31 +01009723 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009724 MSG(_("W18: Invalid character in group name"));
9725 break;
9726 }
9727 }
9728
9729 /*
9730 * First call for this growarray: init growing array.
9731 */
9732 if (highlight_ga.ga_data == NULL)
9733 {
9734 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9735 highlight_ga.ga_growsize = 10;
9736 }
9737
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009738 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009739 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009740 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009741 vim_free(name);
9742 return 0;
9743 }
9744
Bram Moolenaar071d4272004-06-13 20:20:40 +00009745 /*
9746 * Make room for at least one other syntax_highlight entry.
9747 */
9748 if (ga_grow(&highlight_ga, 1) == FAIL)
9749 {
9750 vim_free(name);
9751 return 0;
9752 }
9753
9754 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9755 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9756 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009757#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009758 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9759 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009760# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009761 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009762# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009763#endif
9764 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009765
9766 return highlight_ga.ga_len; /* ID is index plus one */
9767}
9768
9769/*
9770 * When, just after calling syn_add_group(), an error is discovered, this
9771 * function deletes the new name.
9772 */
9773 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009774syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009775{
9776 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009777 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9778 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9779}
9780
9781/*
9782 * Translate a group ID to highlight attributes.
9783 */
9784 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009785syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009786{
9787 int attr;
9788 struct hl_group *sgp;
9789
9790 hl_id = syn_get_final_id(hl_id);
9791 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9792
9793#ifdef FEAT_GUI
9794 /*
9795 * Only use GUI attr when the GUI is being used.
9796 */
9797 if (gui.in_use)
9798 attr = sgp->sg_gui_attr;
9799 else
9800#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009801 if (IS_CTERM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009802 attr = sgp->sg_cterm_attr;
9803 else
9804 attr = sgp->sg_term_attr;
9805
9806 return attr;
9807}
9808
Bram Moolenaarc71053c2017-09-14 00:00:44 +02009809#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009810/*
9811 * Get the GUI colors and attributes for a group ID.
9812 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9813 */
9814 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009815syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009816{
9817 struct hl_group *sgp;
9818
9819 hl_id = syn_get_final_id(hl_id);
9820 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9821
9822 *fgp = sgp->sg_gui_fg;
9823 *bgp = sgp->sg_gui_bg;
9824 return sgp->sg_gui;
9825}
9826#endif
9827
Bram Moolenaard371bbe2017-09-28 22:35:25 +02009828#if defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarc71053c2017-09-14 00:00:44 +02009829 void
9830syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
9831{
9832 struct hl_group *sgp;
9833
9834 hl_id = syn_get_final_id(hl_id);
9835 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9836 *fgp = sgp->sg_cterm_fg - 1;
9837 *bgp = sgp->sg_cterm_bg - 1;
9838}
9839#endif
9840
Bram Moolenaar071d4272004-06-13 20:20:40 +00009841/*
9842 * Translate a group ID to the final group ID (following links).
9843 */
9844 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009845syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009846{
9847 int count;
9848 struct hl_group *sgp;
9849
9850 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9851 return 0; /* Can be called from eval!! */
9852
9853 /*
9854 * Follow links until there is no more.
9855 * Look out for loops! Break after 100 links.
9856 */
9857 for (count = 100; --count >= 0; )
9858 {
9859 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9860 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9861 break;
9862 hl_id = sgp->sg_link;
9863 }
9864
9865 return hl_id;
9866}
9867
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009868#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009869/*
9870 * Call this function just after the GUI has started.
9871 * It finds the font and color handles for the highlighting groups.
9872 */
9873 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009874highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009875{
9876 int idx;
9877
9878 /* First get the colors from the "Normal" and "Menu" group, if set */
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009879# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
9880# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009881 if (USE_24BIT)
9882# endif
9883 set_normal_colors();
9884# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009885
9886 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9887 gui_do_one_color(idx, FALSE, FALSE);
9888
9889 highlight_changed();
9890}
9891
9892 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009893gui_do_one_color(
9894 int idx,
Bram Moolenaar380130f2016-04-22 11:24:43 +02009895 int do_menu UNUSED, /* TRUE: might set the menu font */
9896 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009897{
9898 int didit = FALSE;
9899
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009900# ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009901# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009902 if (gui.in_use)
9903# endif
9904 if (HL_TABLE()[idx].sg_font_name != NULL)
9905 {
9906 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009907 do_tooltip, TRUE);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009908 didit = TRUE;
9909 }
9910# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009911 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9912 {
9913 HL_TABLE()[idx].sg_gui_fg =
9914 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9915 didit = TRUE;
9916 }
9917 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9918 {
9919 HL_TABLE()[idx].sg_gui_bg =
9920 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9921 didit = TRUE;
9922 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009923# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009924 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9925 {
9926 HL_TABLE()[idx].sg_gui_sp =
9927 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9928 didit = TRUE;
9929 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009930# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009931 if (didit) /* need to get a new attr number */
9932 set_hl_attr(idx);
9933}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009934#endif
9935
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009936#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
9937/*
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02009938 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009939 */
9940 static void
9941combine_stl_hlt(
9942 int id,
9943 int id_S,
9944 int id_alt,
9945 int hlcnt,
9946 int i,
9947 int hlf,
9948 int *table)
9949{
9950 struct hl_group *hlt = HL_TABLE();
9951
9952 if (id_alt == 0)
9953 {
9954 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
9955 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
9956 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
9957# if defined(FEAT_GUI) || defined(FEAT_EVAL)
9958 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
9959# endif
9960 }
9961 else
9962 mch_memmove(&hlt[hlcnt + i],
9963 &hlt[id_alt - 1],
9964 sizeof(struct hl_group));
9965 hlt[hlcnt + i].sg_link = 0;
9966
9967 hlt[hlcnt + i].sg_term ^=
9968 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9969 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9970 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9971 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9972 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9973 hlt[hlcnt + i].sg_cterm ^=
9974 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9975 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9976 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9977 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9978 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
9979# if defined(FEAT_GUI) || defined(FEAT_EVAL)
9980 hlt[hlcnt + i].sg_gui ^=
9981 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
9982# endif
9983# ifdef FEAT_GUI
9984 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9985 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9986 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9987 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
9988 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9989 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
9990 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9991 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9992# ifdef FEAT_XFONTSET
9993 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9994 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9995# endif
9996# endif
9997 highlight_ga.ga_len = hlcnt + i + 1;
9998 set_hl_attr(hlcnt + i); /* At long last we can apply */
9999 table[i] = syn_id2attr(hlcnt + i + 1);
10000}
10001#endif
10002
Bram Moolenaar071d4272004-06-13 20:20:40 +000010003/*
10004 * Translate the 'highlight' option into attributes in highlight_attr[] and
10005 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
10006 * corresponding highlights to use on top of HLF_SNC is computed.
10007 * Called only when the 'highlight' option has been changed and upon first
10008 * screen redraw after any :highlight command.
10009 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
10010 */
10011 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010012highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010013{
10014 int hlf;
10015 int i;
10016 char_u *p;
10017 int attr;
10018 char_u *end;
10019 int id;
10020#ifdef USER_HIGHLIGHT
10021 char_u userhl[10];
10022# ifdef FEAT_STL_OPT
10023 int id_SNC = -1;
10024 int id_S = -1;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010025# ifdef FEAT_TERMINAL
10026 int id_ST = -1;
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010027 int id_STNC = -1;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010028# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010029 int hlcnt;
10030# endif
10031#endif
10032 static int hl_flags[HLF_COUNT] = HL_FLAGS;
10033
10034 need_highlight_changed = FALSE;
10035
10036 /*
10037 * Clear all attributes.
10038 */
10039 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
10040 highlight_attr[hlf] = 0;
10041
10042 /*
10043 * First set all attributes to their default value.
10044 * Then use the attributes from the 'highlight' option.
10045 */
10046 for (i = 0; i < 2; ++i)
10047 {
10048 if (i)
10049 p = p_hl;
10050 else
10051 p = get_highlight_default();
10052 if (p == NULL) /* just in case */
10053 continue;
10054
10055 while (*p)
10056 {
10057 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
10058 if (hl_flags[hlf] == *p)
10059 break;
10060 ++p;
10061 if (hlf == (int)HLF_COUNT || *p == NUL)
10062 return FAIL;
10063
10064 /*
10065 * Allow several hl_flags to be combined, like "bu" for
10066 * bold-underlined.
10067 */
10068 attr = 0;
10069 for ( ; *p && *p != ','; ++p) /* parse upto comma */
10070 {
Bram Moolenaar1c465442017-03-12 20:10:05 +010010071 if (VIM_ISWHITE(*p)) /* ignore white space */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010072 continue;
10073
10074 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
10075 return FAIL;
10076
10077 switch (*p)
10078 {
10079 case 'b': attr |= HL_BOLD;
10080 break;
10081 case 'i': attr |= HL_ITALIC;
10082 break;
10083 case '-':
10084 case 'n': /* no highlighting */
10085 break;
10086 case 'r': attr |= HL_INVERSE;
10087 break;
10088 case 's': attr |= HL_STANDOUT;
10089 break;
10090 case 'u': attr |= HL_UNDERLINE;
10091 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +000010092 case 'c': attr |= HL_UNDERCURL;
10093 break;
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +020010094 case 't': attr |= HL_STRIKETHROUGH;
10095 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010096 case ':': ++p; /* highlight group name */
10097 if (attr || *p == NUL) /* no combinations */
10098 return FAIL;
10099 end = vim_strchr(p, ',');
10100 if (end == NULL)
10101 end = p + STRLEN(p);
10102 id = syn_check_group(p, (int)(end - p));
10103 if (id == 0)
10104 return FAIL;
10105 attr = syn_id2attr(id);
10106 p = end - 1;
10107#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
10108 if (hlf == (int)HLF_SNC)
10109 id_SNC = syn_get_final_id(id);
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010110# ifdef FEAT_TERMINAL
10111 else if (hlf == (int)HLF_ST)
10112 id_ST = syn_get_final_id(id);
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010113 else if (hlf == (int)HLF_STNC)
10114 id_STNC = syn_get_final_id(id);
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010115# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010116 else if (hlf == (int)HLF_S)
10117 id_S = syn_get_final_id(id);
10118#endif
10119 break;
10120 default: return FAIL;
10121 }
10122 }
10123 highlight_attr[hlf] = attr;
10124
10125 p = skip_to_option_part(p); /* skip comma and spaces */
10126 }
10127 }
10128
10129#ifdef USER_HIGHLIGHT
10130 /* Setup the user highlights
10131 *
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010132 * Temporarily utilize 28 more hl entries:
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010133 * 9 for User1-User9 combined with StatusLineNC
10134 * 9 for User1-User9 combined with StatusLineTerm
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010135 * 9 for User1-User9 combined with StatusLineTermNC
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010136 * 1 for StatusLine default
10137 * Have to be in there simultaneously in case of table overflows in
10138 * get_attr_entry()
Bram Moolenaar071d4272004-06-13 20:20:40 +000010139 */
10140# ifdef FEAT_STL_OPT
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010141 if (ga_grow(&highlight_ga, 28) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010142 return FAIL;
10143 hlcnt = highlight_ga.ga_len;
Bram Moolenaard6a7b3e2017-08-19 21:35:35 +020010144 if (id_S == -1)
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010145 {
10146 /* Make sure id_S is always valid to simplify code below. Use the last
10147 * entry. */
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010148 vim_memset(&HL_TABLE()[hlcnt + 27], 0, sizeof(struct hl_group));
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010149 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
10150 id_S = hlcnt + 19;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010151 }
10152# endif
10153 for (i = 0; i < 9; i++)
10154 {
10155 sprintf((char *)userhl, "User%d", i + 1);
10156 id = syn_name2id(userhl);
10157 if (id == 0)
10158 {
10159 highlight_user[i] = 0;
10160# ifdef FEAT_STL_OPT
10161 highlight_stlnc[i] = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010162# ifdef FEAT_TERMINAL
10163 highlight_stlterm[i] = 0;
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010164 highlight_stltermnc[i] = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010165# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010166# endif
10167 }
10168 else
10169 {
Bram Moolenaar071d4272004-06-13 20:20:40 +000010170 highlight_user[i] = syn_id2attr(id);
10171# ifdef FEAT_STL_OPT
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010172 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
10173 HLF_SNC, highlight_stlnc);
10174# ifdef FEAT_TERMINAL
10175 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
10176 HLF_ST, highlight_stlterm);
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010177 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
10178 HLF_STNC, highlight_stltermnc);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010179# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010180# endif
10181 }
10182 }
10183# ifdef FEAT_STL_OPT
10184 highlight_ga.ga_len = hlcnt;
10185# endif
10186
10187#endif /* USER_HIGHLIGHT */
10188
10189 return OK;
10190}
10191
Bram Moolenaar4f688582007-07-24 12:34:30 +000010192#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010193
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010010194static void highlight_list(void);
10195static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010196
10197/*
10198 * Handle command line completion for :highlight command.
10199 */
10200 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010201set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010202{
10203 char_u *p;
10204
10205 /* Default: expand group names */
10206 xp->xp_context = EXPAND_HIGHLIGHT;
10207 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +000010208 include_link = 2;
10209 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010210
10211 /* (part of) subcommand already typed */
10212 if (*arg != NUL)
10213 {
10214 p = skiptowhite(arg);
10215 if (*p != NUL) /* past "default" or group name */
10216 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010217 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010218 if (STRNCMP("default", arg, p - arg) == 0)
10219 {
10220 arg = skipwhite(p);
10221 xp->xp_pattern = arg;
10222 p = skiptowhite(arg);
10223 }
10224 if (*p != NUL) /* past group name */
10225 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010226 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010227 if (arg[1] == 'i' && arg[0] == 'N')
10228 highlight_list();
10229 if (STRNCMP("link", arg, p - arg) == 0
10230 || STRNCMP("clear", arg, p - arg) == 0)
10231 {
10232 xp->xp_pattern = skipwhite(p);
10233 p = skiptowhite(xp->xp_pattern);
10234 if (*p != NUL) /* past first group name */
10235 {
10236 xp->xp_pattern = skipwhite(p);
10237 p = skiptowhite(xp->xp_pattern);
10238 }
10239 }
10240 if (*p != NUL) /* past group name(s) */
10241 xp->xp_context = EXPAND_NOTHING;
10242 }
10243 }
10244 }
10245}
10246
10247/*
10248 * List highlighting matches in a nice way.
10249 */
10250 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010251highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010252{
10253 int i;
10254
10255 for (i = 10; --i >= 0; )
Bram Moolenaar8820b482017-03-16 17:23:31 +010010256 highlight_list_two(i, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +000010257 for (i = 40; --i >= 0; )
10258 highlight_list_two(99, 0);
10259}
10260
10261 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010262highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010263{
Bram Moolenaar112f3182012-06-01 13:18:53 +020010264 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010265 msg_clr_eos();
10266 out_flush();
10267 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
10268}
10269
10270#endif /* FEAT_CMDL_COMPL */
10271
10272#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
10273 || defined(FEAT_SIGNS) || defined(PROTO)
10274/*
10275 * Function given to ExpandGeneric() to obtain the list of group names.
Bram Moolenaar071d4272004-06-13 20:20:40 +000010276 */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010277 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010278get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010279{
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010280 return get_highlight_name_ext(xp, idx, TRUE);
10281}
10282
10283/*
10284 * Obtain a highlight group name.
10285 * When "skip_cleared" is TRUE don't return a cleared entry.
10286 */
10287 char_u *
10288get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
10289{
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010290 if (idx < 0)
10291 return NULL;
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010292
10293 /* Items are never removed from the table, skip the ones that were
10294 * cleared. */
10295 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
10296 return (char_u *)"";
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010297
Bram Moolenaar071d4272004-06-13 20:20:40 +000010298#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000010299 if (idx == highlight_ga.ga_len && include_none != 0)
10300 return (char_u *)"none";
10301 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010302 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +000010303 if (idx == highlight_ga.ga_len + include_none + include_default
10304 && include_link != 0)
10305 return (char_u *)"link";
10306 if (idx == highlight_ga.ga_len + include_none + include_default + 1
10307 && include_link != 0)
10308 return (char_u *)"clear";
10309#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +010010310 if (idx >= highlight_ga.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010311 return NULL;
10312 return HL_TABLE()[idx].sg_name;
10313}
10314#endif
10315
Bram Moolenaar4f688582007-07-24 12:34:30 +000010316#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010317/*
10318 * Free all the highlight group fonts.
10319 * Used when quitting for systems which need it.
10320 */
10321 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010322free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010323{
10324 int idx;
10325
10326 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
10327 {
10328 gui_mch_free_font(HL_TABLE()[idx].sg_font);
10329 HL_TABLE()[idx].sg_font = NOFONT;
10330# ifdef FEAT_XFONTSET
10331 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
10332 HL_TABLE()[idx].sg_fontset = NOFONTSET;
10333# endif
10334 }
10335
10336 gui_mch_free_font(gui.norm_font);
10337# ifdef FEAT_XFONTSET
10338 gui_mch_free_fontset(gui.fontset);
10339# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +020010340# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +000010341 gui_mch_free_font(gui.bold_font);
10342 gui_mch_free_font(gui.ital_font);
10343 gui_mch_free_font(gui.boldital_font);
10344# endif
10345}
10346#endif
10347
10348/**************************************
10349 * End of Highlighting stuff *
10350 **************************************/