blob: 98109e77756cbc15f9be3a5bb54426571b284035 [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);
1192 vim_free(block->b_sst_array);
1193 block->b_sst_array = NULL;
1194 block->b_sst_len = 0;
1195 }
1196}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001197/*
1198 * Free b_sst_array[] for buffer "buf".
1199 * Used when syntax items changed to force resyncing everywhere.
1200 */
1201 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001202syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001203{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001204#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001205 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001206#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001207
Bram Moolenaar860cae12010-06-05 23:22:07 +02001208 syn_stack_free_block(block);
1209
Bram Moolenaar071d4272004-06-13 20:20:40 +00001210#ifdef FEAT_FOLDING
1211 /* When using "syntax" fold method, must update all folds. */
1212 FOR_ALL_WINDOWS(wp)
1213 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001214 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001215 foldUpdateAll(wp);
1216 }
1217#endif
1218}
1219
1220/*
1221 * Allocate the syntax state stack for syn_buf when needed.
1222 * If the number of entries in b_sst_array[] is much too big or a bit too
1223 * small, reallocate it.
1224 * Also used to allocate b_sst_array[] for the first time.
1225 */
1226 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001227syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001228{
1229 long len;
1230 synstate_T *to, *from;
1231 synstate_T *sstp;
1232
1233 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1234 if (len < SST_MIN_ENTRIES)
1235 len = SST_MIN_ENTRIES;
1236 else if (len > SST_MAX_ENTRIES)
1237 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001238 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001239 {
1240 /* Allocate 50% too much, to avoid reallocating too often. */
1241 len = syn_buf->b_ml.ml_line_count;
1242 len = (len + len / 2) / SST_DIST + Rows * 2;
1243 if (len < SST_MIN_ENTRIES)
1244 len = SST_MIN_ENTRIES;
1245 else if (len > SST_MAX_ENTRIES)
1246 len = SST_MAX_ENTRIES;
1247
Bram Moolenaar860cae12010-06-05 23:22:07 +02001248 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001249 {
1250 /* When shrinking the array, cleanup the existing stack.
1251 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001252 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001253 && syn_stack_cleanup())
1254 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001255 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1256 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001257 }
1258
1259 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1260 if (sstp == NULL) /* out of memory! */
1261 return;
1262
1263 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001264 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001265 {
1266 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001267 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001268 from = from->sst_next)
1269 {
1270 ++to;
1271 *to = *from;
1272 to->sst_next = to + 1;
1273 }
1274 }
1275 if (to != sstp - 1)
1276 {
1277 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001278 syn_block->b_sst_first = sstp;
1279 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001280 }
1281 else
1282 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001283 syn_block->b_sst_first = NULL;
1284 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001285 }
1286
1287 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001288 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001289 while (++to < sstp + len)
1290 to->sst_next = to + 1;
1291 (sstp + len - 1)->sst_next = NULL;
1292
Bram Moolenaar860cae12010-06-05 23:22:07 +02001293 vim_free(syn_block->b_sst_array);
1294 syn_block->b_sst_array = sstp;
1295 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001296 }
1297}
1298
1299/*
1300 * Check for changes in a buffer to affect stored syntax states. Uses the
1301 * b_mod_* fields.
1302 * Called from update_screen(), before screen is being updated, once for each
1303 * displayed buffer.
1304 */
1305 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001306syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001307{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001308 win_T *wp;
1309
1310 syn_stack_apply_changes_block(&buf->b_s, buf);
1311
1312 FOR_ALL_WINDOWS(wp)
1313 {
1314 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1315 syn_stack_apply_changes_block(wp->w_s, buf);
1316 }
1317}
1318
1319 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001320syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001321{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001322 synstate_T *p, *prev, *np;
1323 linenr_T n;
1324
Bram Moolenaar860cae12010-06-05 23:22:07 +02001325 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001326 return;
1327
1328 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001329 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001330 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001331 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001332 {
1333 n = p->sst_lnum + buf->b_mod_xlines;
1334 if (n <= buf->b_mod_bot)
1335 {
1336 /* this state is inside the changed area, remove it */
1337 np = p->sst_next;
1338 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001339 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001340 else
1341 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001342 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001343 p = np;
1344 continue;
1345 }
1346 /* This state is below the changed area. Remember the line
1347 * that needs to be parsed before this entry can be made valid
1348 * again. */
1349 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1350 {
1351 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1352 p->sst_change_lnum += buf->b_mod_xlines;
1353 else
1354 p->sst_change_lnum = buf->b_mod_top;
1355 }
1356 if (p->sst_change_lnum == 0
1357 || p->sst_change_lnum < buf->b_mod_bot)
1358 p->sst_change_lnum = buf->b_mod_bot;
1359
1360 p->sst_lnum = n;
1361 }
1362 prev = p;
1363 p = p->sst_next;
1364 }
1365}
1366
1367/*
1368 * Reduce the number of entries in the state stack for syn_buf.
1369 * Returns TRUE if at least one entry was freed.
1370 */
1371 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001372syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001373{
1374 synstate_T *p, *prev;
1375 disptick_T tick;
1376 int above;
1377 int dist;
1378 int retval = FALSE;
1379
Bram Moolenaar860cae12010-06-05 23:22:07 +02001380 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001381 return retval;
1382
1383 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001384 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001385 dist = 999999;
1386 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001387 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001388
1389 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001390 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001391 * be removed. Set "above" when the "tick" for the oldest entry is above
1392 * "b_sst_lasttick" (the display tick wraps around).
1393 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001394 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001395 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001396 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001397 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1398 {
1399 if (prev->sst_lnum + dist > p->sst_lnum)
1400 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001401 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001402 {
1403 if (!above || p->sst_tick < tick)
1404 tick = p->sst_tick;
1405 above = TRUE;
1406 }
1407 else if (!above && p->sst_tick < tick)
1408 tick = p->sst_tick;
1409 }
1410 }
1411
1412 /*
1413 * Go through the list to make the entries for the oldest tick at an
1414 * interval of several lines.
1415 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001416 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001417 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1418 {
1419 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1420 {
1421 /* Move this entry from used list to free list */
1422 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001423 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001424 p = prev;
1425 retval = TRUE;
1426 }
1427 }
1428 return retval;
1429}
1430
1431/*
1432 * Free the allocated memory for a syn_state item.
1433 * Move the entry into the free list.
1434 */
1435 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001436syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001437{
1438 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001439 p->sst_next = block->b_sst_firstfree;
1440 block->b_sst_firstfree = p;
1441 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001442}
1443
1444/*
1445 * Find an entry in the list of state stacks at or before "lnum".
1446 * Returns NULL when there is no entry or the first entry is after "lnum".
1447 */
1448 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001449syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001450{
1451 synstate_T *p, *prev;
1452
1453 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001454 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001455 {
1456 if (p->sst_lnum == lnum)
1457 return p;
1458 if (p->sst_lnum > lnum)
1459 break;
1460 }
1461 return prev;
1462}
1463
1464/*
1465 * Try saving the current state in b_sst_array[].
1466 * The current state must be valid for the start of the current_lnum line!
1467 */
1468 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001469store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001470{
1471 int i;
1472 synstate_T *p;
1473 bufstate_T *bp;
1474 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001475 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001476
1477 /*
1478 * If the current state contains a start or end pattern that continues
1479 * from the previous line, we can't use it. Don't store it then.
1480 */
1481 for (i = current_state.ga_len - 1; i >= 0; --i)
1482 {
1483 cur_si = &CUR_STATE(i);
1484 if (cur_si->si_h_startpos.lnum >= current_lnum
1485 || cur_si->si_m_endpos.lnum >= current_lnum
1486 || cur_si->si_h_endpos.lnum >= current_lnum
1487 || (cur_si->si_end_idx
1488 && cur_si->si_eoe_pos.lnum >= current_lnum))
1489 break;
1490 }
1491 if (i >= 0)
1492 {
1493 if (sp != NULL)
1494 {
1495 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001496 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001497 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001498 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001499 else
1500 {
1501 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001502 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001503 if (p->sst_next == sp)
1504 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001505 if (p != NULL) /* just in case */
1506 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001507 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001508 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001509 sp = NULL;
1510 }
1511 }
1512 else if (sp == NULL || sp->sst_lnum != current_lnum)
1513 {
1514 /*
1515 * Add a new entry
1516 */
1517 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001518 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001519 {
1520 (void)syn_stack_cleanup();
1521 /* "sp" may have been moved to the freelist now */
1522 sp = syn_stack_find_entry(current_lnum);
1523 }
1524 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001525 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001526 sp = NULL;
1527 else
1528 {
1529 /* Take the first item from the free list and put it in the used
1530 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001531 p = syn_block->b_sst_firstfree;
1532 syn_block->b_sst_firstfree = p->sst_next;
1533 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001534 if (sp == NULL)
1535 {
1536 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001537 p->sst_next = syn_block->b_sst_first;
1538 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001539 }
1540 else
1541 {
1542 /* insert in list after *sp */
1543 p->sst_next = sp->sst_next;
1544 sp->sst_next = p;
1545 }
1546 sp = p;
1547 sp->sst_stacksize = 0;
1548 sp->sst_lnum = current_lnum;
1549 }
1550 }
1551 if (sp != NULL)
1552 {
1553 /* When overwriting an existing state stack, clear it first */
1554 clear_syn_state(sp);
1555 sp->sst_stacksize = current_state.ga_len;
1556 if (current_state.ga_len > SST_FIX_STATES)
1557 {
1558 /* Need to clear it, might be something remaining from when the
1559 * length was less than SST_FIX_STATES. */
1560 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1561 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1562 sp->sst_stacksize = 0;
1563 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001564 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001565 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1566 }
1567 else
1568 bp = sp->sst_union.sst_stack;
1569 for (i = 0; i < sp->sst_stacksize; ++i)
1570 {
1571 bp[i].bs_idx = CUR_STATE(i).si_idx;
1572 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001573#ifdef FEAT_CONCEAL
1574 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1575 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1576#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001577 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1578 }
1579 sp->sst_next_flags = current_next_flags;
1580 sp->sst_next_list = current_next_list;
1581 sp->sst_tick = display_tick;
1582 sp->sst_change_lnum = 0;
1583 }
1584 current_state_stored = TRUE;
1585 return sp;
1586}
1587
1588/*
1589 * Copy a state stack from "from" in b_sst_array[] to current_state;
1590 */
1591 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001592load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001593{
1594 int i;
1595 bufstate_T *bp;
1596
1597 clear_current_state();
1598 validate_current_state();
1599 keepend_level = -1;
1600 if (from->sst_stacksize
1601 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1602 {
1603 if (from->sst_stacksize > SST_FIX_STATES)
1604 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1605 else
1606 bp = from->sst_union.sst_stack;
1607 for (i = 0; i < from->sst_stacksize; ++i)
1608 {
1609 CUR_STATE(i).si_idx = bp[i].bs_idx;
1610 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001611#ifdef FEAT_CONCEAL
1612 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1613 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1614#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001615 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1616 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1617 keepend_level = i;
1618 CUR_STATE(i).si_ends = FALSE;
1619 CUR_STATE(i).si_m_lnum = 0;
1620 if (CUR_STATE(i).si_idx >= 0)
1621 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001622 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001623 else
1624 CUR_STATE(i).si_next_list = NULL;
1625 update_si_attr(i);
1626 }
1627 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001628 }
1629 current_next_list = from->sst_next_list;
1630 current_next_flags = from->sst_next_flags;
1631 current_lnum = from->sst_lnum;
1632}
1633
1634/*
1635 * Compare saved state stack "*sp" with the current state.
1636 * Return TRUE when they are equal.
1637 */
1638 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001639syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001640{
1641 int i, j;
1642 bufstate_T *bp;
1643 reg_extmatch_T *six, *bsx;
1644
1645 /* First a quick check if the stacks have the same size end nextlist. */
1646 if (sp->sst_stacksize == current_state.ga_len
1647 && sp->sst_next_list == current_next_list)
1648 {
1649 /* Need to compare all states on both stacks. */
1650 if (sp->sst_stacksize > SST_FIX_STATES)
1651 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1652 else
1653 bp = sp->sst_union.sst_stack;
1654
1655 for (i = current_state.ga_len; --i >= 0; )
1656 {
1657 /* If the item has another index the state is different. */
1658 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1659 break;
1660 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1661 {
1662 /* When the extmatch pointers are different, the strings in
1663 * them can still be the same. Check if the extmatch
1664 * references are equal. */
1665 bsx = bp[i].bs_extmatch;
1666 six = CUR_STATE(i).si_extmatch;
1667 /* If one of the extmatch pointers is NULL the states are
1668 * different. */
1669 if (bsx == NULL || six == NULL)
1670 break;
1671 for (j = 0; j < NSUBEXP; ++j)
1672 {
1673 /* Check each referenced match string. They must all be
1674 * equal. */
1675 if (bsx->matches[j] != six->matches[j])
1676 {
1677 /* If the pointer is different it can still be the
1678 * same text. Compare the strings, ignore case when
1679 * the start item has the sp_ic flag set. */
1680 if (bsx->matches[j] == NULL
1681 || six->matches[j] == NULL)
1682 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001683 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001684 ? MB_STRICMP(bsx->matches[j],
1685 six->matches[j]) != 0
1686 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1687 break;
1688 }
1689 }
1690 if (j != NSUBEXP)
1691 break;
1692 }
1693 }
1694 if (i < 0)
1695 return TRUE;
1696 }
1697 return FALSE;
1698}
1699
1700/*
1701 * We stop parsing syntax above line "lnum". If the stored state at or below
1702 * this line depended on a change before it, it now depends on the line below
1703 * the last parsed line.
1704 * The window looks like this:
1705 * line which changed
1706 * displayed line
1707 * displayed line
1708 * lnum -> line below window
1709 */
1710 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001711syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001712{
1713 synstate_T *sp;
1714
1715 sp = syn_stack_find_entry(lnum);
1716 if (sp != NULL && sp->sst_lnum < lnum)
1717 sp = sp->sst_next;
1718
1719 if (sp != NULL && sp->sst_change_lnum != 0)
1720 sp->sst_change_lnum = lnum;
1721}
1722
1723/*
1724 * End of handling of the state stack.
1725 ****************************************/
1726
1727 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001728invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001729{
1730 clear_current_state();
1731 current_state.ga_itemsize = 0; /* mark current_state invalid */
1732 current_next_list = NULL;
1733 keepend_level = -1;
1734}
1735
1736 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001737validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001738{
1739 current_state.ga_itemsize = sizeof(stateitem_T);
1740 current_state.ga_growsize = 3;
1741}
1742
1743/*
1744 * Return TRUE if the syntax at start of lnum changed since last time.
1745 * This will only be called just after get_syntax_attr() for the previous
1746 * line, to check if the next line needs to be redrawn too.
1747 */
1748 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001749syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001750{
1751 int retval = TRUE;
1752 synstate_T *sp;
1753
Bram Moolenaar071d4272004-06-13 20:20:40 +00001754 /*
1755 * Check the state stack when:
1756 * - lnum is just below the previously syntaxed line.
1757 * - lnum is not before the lines with saved states.
1758 * - lnum is not past the lines with saved states.
1759 * - lnum is at or before the last changed line.
1760 */
1761 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1762 {
1763 sp = syn_stack_find_entry(lnum);
1764 if (sp != NULL && sp->sst_lnum == lnum)
1765 {
1766 /*
1767 * finish the previous line (needed when not all of the line was
1768 * drawn)
1769 */
1770 (void)syn_finish_line(FALSE);
1771
1772 /*
1773 * Compare the current state with the previously saved state of
1774 * the line.
1775 */
1776 if (syn_stack_equal(sp))
1777 retval = FALSE;
1778
1779 /*
1780 * Store the current state in b_sst_array[] for later use.
1781 */
1782 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001783 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001784 }
1785 }
1786
Bram Moolenaar071d4272004-06-13 20:20:40 +00001787 return retval;
1788}
1789
1790/*
1791 * Finish the current line.
1792 * This doesn't return any attributes, it only gets the state at the end of
1793 * the line. It can start anywhere in the line, as long as the current state
1794 * is valid.
1795 */
1796 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001797syn_finish_line(
1798 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001799{
1800 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001801 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001802
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001803 while (!current_finished)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001804 {
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001805 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1806 /*
1807 * When syncing, and found some item, need to check the item.
1808 */
1809 if (syncing && current_state.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001810 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811 /*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001812 * Check for match with sync item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001813 */
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001814 cur_si = &CUR_STATE(current_state.ga_len - 1);
1815 if (cur_si->si_idx >= 0
1816 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1817 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1818 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001819
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001820 /* syn_current_attr() will have skipped the check for an item
1821 * that ends here, need to do that now. Be careful not to go
1822 * past the NUL. */
1823 prev_current_col = current_col;
1824 if (syn_getcurline()[current_col] != NUL)
1825 ++current_col;
1826 check_state_ends();
1827 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001828 }
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001829 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001830 }
1831 return FALSE;
1832}
1833
1834/*
1835 * Return highlight attributes for next character.
1836 * Must first call syntax_start() once for the line.
1837 * "col" is normally 0 for the first use in a line, and increments by one each
1838 * time. It's allowed to skip characters and to stop before the end of the
1839 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001840 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1841 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001842 */
1843 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001844get_syntax_attr(
1845 colnr_T col,
1846 int *can_spell,
1847 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001848{
1849 int attr = 0;
1850
Bram Moolenaar349955a2007-08-14 21:07:36 +00001851 if (can_spell != NULL)
1852 /* Default: Only do spelling when there is no @Spell cluster or when
1853 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001854 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1855 ? (syn_block->b_spell_cluster_id == 0)
1856 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001857
Bram Moolenaar071d4272004-06-13 20:20:40 +00001858 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001859 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001860 return 0;
1861
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001862 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001863 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001864 {
1865 clear_current_state();
1866#ifdef FEAT_EVAL
1867 current_id = 0;
1868 current_trans_id = 0;
1869#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001870#ifdef FEAT_CONCEAL
1871 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001872 current_seqnr = 0;
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001873#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001874 return 0;
1875 }
1876
Bram Moolenaar071d4272004-06-13 20:20:40 +00001877 /* Make sure current_state is valid */
1878 if (INVALID_STATE(&current_state))
1879 validate_current_state();
1880
1881 /*
1882 * Skip from the current column to "col", get the attributes for "col".
1883 */
1884 while (current_col <= col)
1885 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001886 attr = syn_current_attr(FALSE, TRUE, can_spell,
1887 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001888 ++current_col;
1889 }
1890
Bram Moolenaar071d4272004-06-13 20:20:40 +00001891 return attr;
1892}
1893
1894/*
1895 * Get syntax attributes for current_lnum, current_col.
1896 */
1897 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001898syn_current_attr(
1899 int syncing, /* When 1: called for syncing */
1900 int displaying, /* result will be displayed */
1901 int *can_spell, /* return: do spell checking */
1902 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001903{
1904 int syn_id;
1905 lpos_T endpos; /* was: char_u *endp; */
1906 lpos_T hl_startpos; /* was: int hl_startcol; */
1907 lpos_T hl_endpos;
1908 lpos_T eos_pos; /* end-of-start match (start region) */
1909 lpos_T eoe_pos; /* end-of-end pattern */
1910 int end_idx; /* group ID for end pattern */
1911 int idx;
1912 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001913 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001914 int startcol;
1915 int endcol;
1916 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001917 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001918 short *next_list;
1919 int found_match; /* found usable match */
1920 static int try_next_column = FALSE; /* must try in next col */
1921 int do_keywords;
1922 regmmatch_T regmatch;
1923 lpos_T pos;
1924 int lc_col;
1925 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001926 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001927 char_u *line; /* current line. NOTE: becomes invalid after
1928 looking for a pattern match! */
1929
1930 /* variables for zero-width matches that have a "nextgroup" argument */
1931 int keep_next_list;
1932 int zero_width_next_list = FALSE;
1933 garray_T zero_width_next_ga;
1934
1935 /*
1936 * No character, no attributes! Past end of line?
1937 * Do try matching with an empty line (could be the start of a region).
1938 */
1939 line = syn_getcurline();
1940 if (line[current_col] == NUL && current_col != 0)
1941 {
1942 /*
1943 * If we found a match after the last column, use it.
1944 */
1945 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1946 && next_match_col != MAXCOL)
1947 (void)push_next_match(NULL);
1948
1949 current_finished = TRUE;
1950 current_state_stored = FALSE;
1951 return 0;
1952 }
1953
1954 /* if the current or next character is NUL, we will finish the line now */
1955 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1956 {
1957 current_finished = TRUE;
1958 current_state_stored = FALSE;
1959 }
1960
1961 /*
1962 * When in the previous column there was a match but it could not be used
1963 * (empty match or already matched in this column) need to try again in
1964 * the next column.
1965 */
1966 if (try_next_column)
1967 {
1968 next_match_idx = -1;
1969 try_next_column = FALSE;
1970 }
1971
1972 /* Only check for keywords when not syncing and there are some. */
1973 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001974 && (syn_block->b_keywtab.ht_used > 0
1975 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001976
1977 /* Init the list of zero-width matches with a nextlist. This is used to
1978 * avoid matching the same item in the same position twice. */
1979 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1980
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001981 /* use syntax iskeyword option */
1982 save_chartab(buf_chartab);
1983
Bram Moolenaar071d4272004-06-13 20:20:40 +00001984 /*
1985 * Repeat matching keywords and patterns, to find contained items at the
1986 * same column. This stops when there are no extra matches at the current
1987 * column.
1988 */
1989 do
1990 {
1991 found_match = FALSE;
1992 keep_next_list = FALSE;
1993 syn_id = 0;
1994
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001995
Bram Moolenaar071d4272004-06-13 20:20:40 +00001996 /*
1997 * 1. Check for a current state.
1998 * Only when there is no current state, or if the current state may
1999 * contain other things, we need to check for keywords and patterns.
2000 * Always need to check for contained items if some item has the
2001 * "containedin" argument (takes extra time!).
2002 */
2003 if (current_state.ga_len)
2004 cur_si = &CUR_STATE(current_state.ga_len - 1);
2005 else
2006 cur_si = NULL;
2007
Bram Moolenaar860cae12010-06-05 23:22:07 +02002008 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002009 || cur_si->si_cont_list != NULL)
2010 {
2011 /*
2012 * 2. Check for keywords, if on a keyword char after a non-keyword
2013 * char. Don't do this when syncing.
2014 */
2015 if (do_keywords)
2016 {
2017 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002018 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002019 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002020 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002021#ifdef FEAT_MBYTE
2022 - (has_mbyte
2023 ? (*mb_head_off)(line, line + current_col - 1)
2024 : 0)
2025#endif
2026 , syn_buf)))
2027 {
2028 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002029 &endcol, &flags, &next_list, cur_si,
2030 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002031 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002032 {
2033 if (push_current_state(KEYWORD_IDX) == OK)
2034 {
2035 cur_si = &CUR_STATE(current_state.ga_len - 1);
2036 cur_si->si_m_startcol = current_col;
2037 cur_si->si_h_startpos.lnum = current_lnum;
2038 cur_si->si_h_startpos.col = 0; /* starts right away */
2039 cur_si->si_m_endpos.lnum = current_lnum;
2040 cur_si->si_m_endpos.col = endcol;
2041 cur_si->si_h_endpos.lnum = current_lnum;
2042 cur_si->si_h_endpos.col = endcol;
2043 cur_si->si_ends = TRUE;
2044 cur_si->si_end_idx = 0;
2045 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002046#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002047 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002048 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002049 if (current_state.ga_len > 1)
2050 cur_si->si_flags |=
2051 CUR_STATE(current_state.ga_len - 2).si_flags
2052 & HL_CONCEAL;
2053#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002054 cur_si->si_id = syn_id;
2055 cur_si->si_trans_id = syn_id;
2056 if (flags & HL_TRANSP)
2057 {
2058 if (current_state.ga_len < 2)
2059 {
2060 cur_si->si_attr = 0;
2061 cur_si->si_trans_id = 0;
2062 }
2063 else
2064 {
2065 cur_si->si_attr = CUR_STATE(
2066 current_state.ga_len - 2).si_attr;
2067 cur_si->si_trans_id = CUR_STATE(
2068 current_state.ga_len - 2).si_trans_id;
2069 }
2070 }
2071 else
2072 cur_si->si_attr = syn_id2attr(syn_id);
2073 cur_si->si_cont_list = NULL;
2074 cur_si->si_next_list = next_list;
2075 check_keepend();
2076 }
2077 else
2078 vim_free(next_list);
2079 }
2080 }
2081 }
2082
2083 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002084 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002085 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002086 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002087 {
2088 /*
2089 * If we didn't check for a match yet, or we are past it, check
2090 * for any match with a pattern.
2091 */
2092 if (next_match_idx < 0 || next_match_col < (int)current_col)
2093 {
2094 /*
2095 * Check all relevant patterns for a match at this
2096 * position. This is complicated, because matching with a
2097 * pattern takes quite a bit of time, thus we want to
2098 * avoid doing it when it's not needed.
2099 */
2100 next_match_idx = 0; /* no match in this line yet */
2101 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002102 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002103 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002104 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002105 if ( spp->sp_syncing == syncing
2106 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2107 && (spp->sp_type == SPTYPE_MATCH
2108 || spp->sp_type == SPTYPE_START)
2109 && (current_next_list != NULL
2110 ? in_id_list(NULL, current_next_list,
2111 &spp->sp_syn, 0)
2112 : (cur_si == NULL
2113 ? !(spp->sp_flags & HL_CONTAINED)
2114 : in_id_list(cur_si,
2115 cur_si->si_cont_list, &spp->sp_syn,
2116 spp->sp_flags & HL_CONTAINED))))
2117 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002118 int r;
2119
Bram Moolenaar071d4272004-06-13 20:20:40 +00002120 /* If we already tried matching in this line, and
2121 * there isn't a match before next_match_col, skip
2122 * this item. */
2123 if (spp->sp_line_id == current_line_id
2124 && spp->sp_startcol >= next_match_col)
2125 continue;
2126 spp->sp_line_id = current_line_id;
2127
2128 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2129 if (lc_col < 0)
2130 lc_col = 0;
2131
2132 regmatch.rmm_ic = spp->sp_ic;
2133 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002134 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002135 current_lnum,
2136 (colnr_T)lc_col,
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002137 IF_SYN_TIME(&spp->sp_time));
2138 spp->sp_prog = regmatch.regprog;
2139 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002140 {
2141 /* no match in this line, try another one */
2142 spp->sp_startcol = MAXCOL;
2143 continue;
2144 }
2145
2146 /*
2147 * Compute the first column of the match.
2148 */
2149 syn_add_start_off(&pos, &regmatch,
2150 spp, SPO_MS_OFF, -1);
2151 if (pos.lnum > current_lnum)
2152 {
2153 /* must have used end of match in a next line,
2154 * we can't handle that */
2155 spp->sp_startcol = MAXCOL;
2156 continue;
2157 }
2158 startcol = pos.col;
2159
2160 /* remember the next column where this pattern
2161 * matches in the current line */
2162 spp->sp_startcol = startcol;
2163
2164 /*
2165 * If a previously found match starts at a lower
2166 * column number, don't use this one.
2167 */
2168 if (startcol >= next_match_col)
2169 continue;
2170
2171 /*
2172 * If we matched this pattern at this position
2173 * before, skip it. Must retry in the next
2174 * column, because it may match from there.
2175 */
2176 if (did_match_already(idx, &zero_width_next_ga))
2177 {
2178 try_next_column = TRUE;
2179 continue;
2180 }
2181
2182 endpos.lnum = regmatch.endpos[0].lnum;
2183 endpos.col = regmatch.endpos[0].col;
2184
2185 /* Compute the highlight start. */
2186 syn_add_start_off(&hl_startpos, &regmatch,
2187 spp, SPO_HS_OFF, -1);
2188
2189 /* Compute the region start. */
2190 /* Default is to use the end of the match. */
2191 syn_add_end_off(&eos_pos, &regmatch,
2192 spp, SPO_RS_OFF, 0);
2193
2194 /*
2195 * Grab the external submatches before they get
2196 * overwritten. Reference count doesn't change.
2197 */
2198 unref_extmatch(cur_extmatch);
2199 cur_extmatch = re_extmatch_out;
2200 re_extmatch_out = NULL;
2201
2202 flags = 0;
2203 eoe_pos.lnum = 0; /* avoid warning */
2204 eoe_pos.col = 0;
2205 end_idx = 0;
2206 hl_endpos.lnum = 0;
2207
2208 /*
2209 * For a "oneline" the end must be found in the
2210 * same line too. Search for it after the end of
2211 * the match with the start pattern. Set the
2212 * resulting end positions at the same time.
2213 */
2214 if (spp->sp_type == SPTYPE_START
2215 && (spp->sp_flags & HL_ONELINE))
2216 {
2217 lpos_T startpos;
2218
2219 startpos = endpos;
2220 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2221 &flags, &eoe_pos, &end_idx, cur_extmatch);
2222 if (endpos.lnum == 0)
2223 continue; /* not found */
2224 }
2225
2226 /*
2227 * For a "match" the size must be > 0 after the
2228 * end offset needs has been added. Except when
2229 * syncing.
2230 */
2231 else if (spp->sp_type == SPTYPE_MATCH)
2232 {
2233 syn_add_end_off(&hl_endpos, &regmatch, spp,
2234 SPO_HE_OFF, 0);
2235 syn_add_end_off(&endpos, &regmatch, spp,
2236 SPO_ME_OFF, 0);
2237 if (endpos.lnum == current_lnum
2238 && (int)endpos.col + syncing < startcol)
2239 {
2240 /*
2241 * If an empty string is matched, may need
2242 * to try matching again at next column.
2243 */
2244 if (regmatch.startpos[0].col
2245 == regmatch.endpos[0].col)
2246 try_next_column = TRUE;
2247 continue;
2248 }
2249 }
2250
2251 /*
2252 * keep the best match so far in next_match_*
2253 */
2254 /* Highlighting must start after startpos and end
2255 * before endpos. */
2256 if (hl_startpos.lnum == current_lnum
2257 && (int)hl_startpos.col < startcol)
2258 hl_startpos.col = startcol;
2259 limit_pos_zero(&hl_endpos, &endpos);
2260
2261 next_match_idx = idx;
2262 next_match_col = startcol;
2263 next_match_m_endpos = endpos;
2264 next_match_h_endpos = hl_endpos;
2265 next_match_h_startpos = hl_startpos;
2266 next_match_flags = flags;
2267 next_match_eos_pos = eos_pos;
2268 next_match_eoe_pos = eoe_pos;
2269 next_match_end_idx = end_idx;
2270 unref_extmatch(next_match_extmatch);
2271 next_match_extmatch = cur_extmatch;
2272 cur_extmatch = NULL;
2273 }
2274 }
2275 }
2276
2277 /*
2278 * If we found a match at the current column, use it.
2279 */
2280 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2281 {
2282 synpat_T *lspp;
2283
2284 /* When a zero-width item matched which has a nextgroup,
2285 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002286 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002287 if (next_match_m_endpos.lnum == current_lnum
2288 && next_match_m_endpos.col == current_col
2289 && lspp->sp_next_list != NULL)
2290 {
2291 current_next_list = lspp->sp_next_list;
2292 current_next_flags = lspp->sp_flags;
2293 keep_next_list = TRUE;
2294 zero_width_next_list = TRUE;
2295
2296 /* Add the index to a list, so that we can check
2297 * later that we don't match it again (and cause an
2298 * endless loop). */
2299 if (ga_grow(&zero_width_next_ga, 1) == OK)
2300 {
2301 ((int *)(zero_width_next_ga.ga_data))
2302 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002303 }
2304 next_match_idx = -1;
2305 }
2306 else
2307 cur_si = push_next_match(cur_si);
2308 found_match = TRUE;
2309 }
2310 }
2311 }
2312
2313 /*
2314 * Handle searching for nextgroup match.
2315 */
2316 if (current_next_list != NULL && !keep_next_list)
2317 {
2318 /*
2319 * If a nextgroup was not found, continue looking for one if:
2320 * - this is an empty line and the "skipempty" option was given
2321 * - we are on white space and the "skipwhite" option was given
2322 */
2323 if (!found_match)
2324 {
2325 line = syn_getcurline();
2326 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002327 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002328 || ((current_next_flags & HL_SKIPEMPTY)
2329 && *line == NUL))
2330 break;
2331 }
2332
2333 /*
2334 * If a nextgroup was found: Use it, and continue looking for
2335 * contained matches.
2336 * If a nextgroup was not found: Continue looking for a normal
2337 * match.
2338 * When did set current_next_list for a zero-width item and no
2339 * match was found don't loop (would get stuck).
2340 */
2341 current_next_list = NULL;
2342 next_match_idx = -1;
2343 if (!zero_width_next_list)
2344 found_match = TRUE;
2345 }
2346
2347 } while (found_match);
2348
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002349 restore_chartab(buf_chartab);
2350
Bram Moolenaar071d4272004-06-13 20:20:40 +00002351 /*
2352 * Use attributes from the current state, if within its highlighting.
2353 * If not, use attributes from the current-but-one state, etc.
2354 */
2355 current_attr = 0;
2356#ifdef FEAT_EVAL
2357 current_id = 0;
2358 current_trans_id = 0;
2359#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002360#ifdef FEAT_CONCEAL
2361 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02002362 current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002363#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002364 if (cur_si != NULL)
2365 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002366#ifndef FEAT_EVAL
2367 int current_trans_id = 0;
2368#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002369 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2370 {
2371 sip = &CUR_STATE(idx);
2372 if ((current_lnum > sip->si_h_startpos.lnum
2373 || (current_lnum == sip->si_h_startpos.lnum
2374 && current_col >= sip->si_h_startpos.col))
2375 && (sip->si_h_endpos.lnum == 0
2376 || current_lnum < sip->si_h_endpos.lnum
2377 || (current_lnum == sip->si_h_endpos.lnum
2378 && current_col < sip->si_h_endpos.col)))
2379 {
2380 current_attr = sip->si_attr;
2381#ifdef FEAT_EVAL
2382 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002383#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002384 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002385#ifdef FEAT_CONCEAL
2386 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002387 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002388 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002389#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002390 break;
2391 }
2392 }
2393
Bram Moolenaar217ad922005-03-20 22:37:15 +00002394 if (can_spell != NULL)
2395 {
2396 struct sp_syn sps;
2397
2398 /*
2399 * set "can_spell" to TRUE if spell checking is supposed to be
2400 * done in the current item.
2401 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002402 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002403 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002404 /* There is no @Spell cluster: Do spelling for items without
2405 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002406 if (syn_block->b_nospell_cluster_id == 0
2407 || current_trans_id == 0)
2408 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002409 else
2410 {
2411 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002412 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002413 sps.cont_in_list = NULL;
2414 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2415 }
2416 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002417 else
2418 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002419 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002420 * the @Spell cluster. But not when @NoSpell is also there.
2421 * At the toplevel only spell check when ":syn spell toplevel"
2422 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002423 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002424 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002425 else
2426 {
2427 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002428 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002429 sps.cont_in_list = NULL;
2430 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2431
Bram Moolenaar860cae12010-06-05 23:22:07 +02002432 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002433 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002434 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002435 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2436 *can_spell = FALSE;
2437 }
2438 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002439 }
2440 }
2441
2442
Bram Moolenaar071d4272004-06-13 20:20:40 +00002443 /*
2444 * Check for end of current state (and the states before it) at the
2445 * next column. Don't do this for syncing, because we would miss a
2446 * single character match.
2447 * First check if the current state ends at the current column. It
2448 * may be for an empty match and a containing item might end in the
2449 * current column.
2450 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002451 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002452 {
2453 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002454 if (current_state.ga_len > 0
2455 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002456 {
2457 ++current_col;
2458 check_state_ends();
2459 --current_col;
2460 }
2461 }
2462 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002463 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002464 /* Default: Only do spelling when there is no @Spell cluster or when
2465 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002466 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2467 ? (syn_block->b_spell_cluster_id == 0)
2468 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002469
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002470 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002471 if (current_next_list != NULL
2472 && syn_getcurline()[current_col + 1] == NUL
2473 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2474 current_next_list = NULL;
2475
2476 if (zero_width_next_ga.ga_len > 0)
2477 ga_clear(&zero_width_next_ga);
2478
2479 /* No longer need external matches. But keep next_match_extmatch. */
2480 unref_extmatch(re_extmatch_out);
2481 re_extmatch_out = NULL;
2482 unref_extmatch(cur_extmatch);
2483
2484 return current_attr;
2485}
2486
2487
2488/*
2489 * Check if we already matched pattern "idx" at the current column.
2490 */
2491 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002492did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002493{
2494 int i;
2495
2496 for (i = current_state.ga_len; --i >= 0; )
2497 if (CUR_STATE(i).si_m_startcol == (int)current_col
2498 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2499 && CUR_STATE(i).si_idx == idx)
2500 return TRUE;
2501
2502 /* Zero-width matches with a nextgroup argument are not put on the syntax
2503 * stack, and can only be matched once anyway. */
2504 for (i = gap->ga_len; --i >= 0; )
2505 if (((int *)(gap->ga_data))[i] == idx)
2506 return TRUE;
2507
2508 return FALSE;
2509}
2510
2511/*
2512 * Push the next match onto the stack.
2513 */
2514 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002515push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002516{
2517 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002518#ifdef FEAT_CONCEAL
2519 int save_flags;
2520#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002521
Bram Moolenaar860cae12010-06-05 23:22:07 +02002522 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002523
2524 /*
2525 * Push the item in current_state stack;
2526 */
2527 if (push_current_state(next_match_idx) == OK)
2528 {
2529 /*
2530 * If it's a start-skip-end type that crosses lines, figure out how
2531 * much it continues in this line. Otherwise just fill in the length.
2532 */
2533 cur_si = &CUR_STATE(current_state.ga_len - 1);
2534 cur_si->si_h_startpos = next_match_h_startpos;
2535 cur_si->si_m_startcol = current_col;
2536 cur_si->si_m_lnum = current_lnum;
2537 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002538#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002539 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002540 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002541 if (current_state.ga_len > 1)
2542 cur_si->si_flags |=
2543 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2544#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002545 cur_si->si_next_list = spp->sp_next_list;
2546 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2547 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2548 {
2549 /* Try to find the end pattern in the current line */
2550 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2551 check_keepend();
2552 }
2553 else
2554 {
2555 cur_si->si_m_endpos = next_match_m_endpos;
2556 cur_si->si_h_endpos = next_match_h_endpos;
2557 cur_si->si_ends = TRUE;
2558 cur_si->si_flags |= next_match_flags;
2559 cur_si->si_eoe_pos = next_match_eoe_pos;
2560 cur_si->si_end_idx = next_match_end_idx;
2561 }
2562 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2563 keepend_level = current_state.ga_len - 1;
2564 check_keepend();
2565 update_si_attr(current_state.ga_len - 1);
2566
Bram Moolenaar860cae12010-06-05 23:22:07 +02002567#ifdef FEAT_CONCEAL
2568 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2569#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002570 /*
2571 * If the start pattern has another highlight group, push another item
2572 * on the stack for the start pattern.
2573 */
2574 if ( spp->sp_type == SPTYPE_START
2575 && spp->sp_syn_match_id != 0
2576 && push_current_state(next_match_idx) == OK)
2577 {
2578 cur_si = &CUR_STATE(current_state.ga_len - 1);
2579 cur_si->si_h_startpos = next_match_h_startpos;
2580 cur_si->si_m_startcol = current_col;
2581 cur_si->si_m_lnum = current_lnum;
2582 cur_si->si_m_endpos = next_match_eos_pos;
2583 cur_si->si_h_endpos = next_match_eos_pos;
2584 cur_si->si_ends = TRUE;
2585 cur_si->si_end_idx = 0;
2586 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002587#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002588 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002589 cur_si->si_flags |= save_flags;
2590 if (cur_si->si_flags & HL_CONCEALENDS)
2591 cur_si->si_flags |= HL_CONCEAL;
2592#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002593 cur_si->si_next_list = NULL;
2594 check_keepend();
2595 update_si_attr(current_state.ga_len - 1);
2596 }
2597 }
2598
2599 next_match_idx = -1; /* try other match next time */
2600
2601 return cur_si;
2602}
2603
2604/*
2605 * Check for end of current state (and the states before it).
2606 */
2607 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002608check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002609{
2610 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002611 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002612
2613 cur_si = &CUR_STATE(current_state.ga_len - 1);
2614 for (;;)
2615 {
2616 if (cur_si->si_ends
2617 && (cur_si->si_m_endpos.lnum < current_lnum
2618 || (cur_si->si_m_endpos.lnum == current_lnum
2619 && cur_si->si_m_endpos.col <= current_col)))
2620 {
2621 /*
2622 * If there is an end pattern group ID, highlight the end pattern
2623 * now. No need to pop the current item from the stack.
2624 * Only do this if the end pattern continues beyond the current
2625 * position.
2626 */
2627 if (cur_si->si_end_idx
2628 && (cur_si->si_eoe_pos.lnum > current_lnum
2629 || (cur_si->si_eoe_pos.lnum == current_lnum
2630 && cur_si->si_eoe_pos.col > current_col)))
2631 {
2632 cur_si->si_idx = cur_si->si_end_idx;
2633 cur_si->si_end_idx = 0;
2634 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2635 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2636 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002637#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002638 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002639 if (cur_si->si_flags & HL_CONCEALENDS)
2640 cur_si->si_flags |= HL_CONCEAL;
2641#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002642 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002643
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002644 /* nextgroup= should not match in the end pattern */
2645 current_next_list = NULL;
2646
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002647 /* what matches next may be different now, clear it */
2648 next_match_idx = 0;
2649 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002650 break;
2651 }
2652 else
2653 {
2654 /* handle next_list, unless at end of line and no "skipnl" or
2655 * "skipempty" */
2656 current_next_list = cur_si->si_next_list;
2657 current_next_flags = cur_si->si_flags;
2658 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2659 && syn_getcurline()[current_col] == NUL)
2660 current_next_list = NULL;
2661
2662 /* When the ended item has "extend", another item with
2663 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002664 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002665
2666 pop_current_state();
2667
2668 if (current_state.ga_len == 0)
2669 break;
2670
Bram Moolenaar81993f42008-01-11 20:27:45 +00002671 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002672 {
2673 syn_update_ends(FALSE);
2674 if (current_state.ga_len == 0)
2675 break;
2676 }
2677
2678 cur_si = &CUR_STATE(current_state.ga_len - 1);
2679
2680 /*
2681 * Only for a region the search for the end continues after
2682 * the end of the contained item. If the contained match
2683 * included the end-of-line, break here, the region continues.
2684 * Don't do this when:
2685 * - "keepend" is used for the contained item
2686 * - not at the end of the line (could be end="x$"me=e-1).
2687 * - "excludenl" is used (HL_HAS_EOL won't be set)
2688 */
2689 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002690 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002691 == SPTYPE_START
2692 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2693 {
2694 update_si_end(cur_si, (int)current_col, TRUE);
2695 check_keepend();
2696 if ((current_next_flags & HL_HAS_EOL)
2697 && keepend_level < 0
2698 && syn_getcurline()[current_col] == NUL)
2699 break;
2700 }
2701 }
2702 }
2703 else
2704 break;
2705 }
2706}
2707
2708/*
2709 * Update an entry in the current_state stack for a match or region. This
2710 * fills in si_attr, si_next_list and si_cont_list.
2711 */
2712 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002713update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002714{
2715 stateitem_T *sip = &CUR_STATE(idx);
2716 synpat_T *spp;
2717
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002718 /* This should not happen... */
2719 if (sip->si_idx < 0)
2720 return;
2721
Bram Moolenaar860cae12010-06-05 23:22:07 +02002722 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002723 if (sip->si_flags & HL_MATCH)
2724 sip->si_id = spp->sp_syn_match_id;
2725 else
2726 sip->si_id = spp->sp_syn.id;
2727 sip->si_attr = syn_id2attr(sip->si_id);
2728 sip->si_trans_id = sip->si_id;
2729 if (sip->si_flags & HL_MATCH)
2730 sip->si_cont_list = NULL;
2731 else
2732 sip->si_cont_list = spp->sp_cont_list;
2733
2734 /*
2735 * For transparent items, take attr from outer item.
2736 * Also take cont_list, if there is none.
2737 * Don't do this for the matchgroup of a start or end pattern.
2738 */
2739 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2740 {
2741 if (idx == 0)
2742 {
2743 sip->si_attr = 0;
2744 sip->si_trans_id = 0;
2745 if (sip->si_cont_list == NULL)
2746 sip->si_cont_list = ID_LIST_ALL;
2747 }
2748 else
2749 {
2750 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2751 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002752 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2753 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002754 if (sip->si_cont_list == NULL)
2755 {
2756 sip->si_flags |= HL_TRANS_CONT;
2757 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2758 }
2759 }
2760 }
2761}
2762
2763/*
2764 * Check the current stack for patterns with "keepend" flag.
2765 * Propagate the match-end to contained items, until a "skipend" item is found.
2766 */
2767 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002768check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002769{
2770 int i;
2771 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002772 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002773 stateitem_T *sip;
2774
2775 /*
2776 * This check can consume a lot of time; only do it from the level where
2777 * there really is a keepend.
2778 */
2779 if (keepend_level < 0)
2780 return;
2781
2782 /*
2783 * Find the last index of an "extend" item. "keepend" items before that
2784 * won't do anything. If there is no "extend" item "i" will be
2785 * "keepend_level" and all "keepend" items will work normally.
2786 */
2787 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2788 if (CUR_STATE(i).si_flags & HL_EXTEND)
2789 break;
2790
2791 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002792 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002793 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002794 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002795 for ( ; i < current_state.ga_len; ++i)
2796 {
2797 sip = &CUR_STATE(i);
2798 if (maxpos.lnum != 0)
2799 {
2800 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002801 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002802 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2803 sip->si_ends = TRUE;
2804 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002805 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2806 {
2807 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002808 || maxpos.lnum > sip->si_m_endpos.lnum
2809 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002810 && maxpos.col > sip->si_m_endpos.col))
2811 maxpos = sip->si_m_endpos;
2812 if (maxpos_h.lnum == 0
2813 || maxpos_h.lnum > sip->si_h_endpos.lnum
2814 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2815 && maxpos_h.col > sip->si_h_endpos.col))
2816 maxpos_h = sip->si_h_endpos;
2817 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002818 }
2819}
2820
2821/*
2822 * Update an entry in the current_state stack for a start-skip-end pattern.
2823 * This finds the end of the current item, if it's in the current line.
2824 *
2825 * Return the flags for the matched END.
2826 */
2827 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002828update_si_end(
2829 stateitem_T *sip,
2830 int startcol, /* where to start searching for the end */
2831 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002832{
2833 lpos_T startpos;
2834 lpos_T endpos;
2835 lpos_T hl_endpos;
2836 lpos_T end_endpos;
2837 int end_idx;
2838
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002839 /* return quickly for a keyword */
2840 if (sip->si_idx < 0)
2841 return;
2842
Bram Moolenaar071d4272004-06-13 20:20:40 +00002843 /* Don't update when it's already done. Can be a match of an end pattern
2844 * that started in a previous line. Watch out: can also be a "keepend"
2845 * from a containing item. */
2846 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2847 return;
2848
2849 /*
2850 * We need to find the end of the region. It may continue in the next
2851 * line.
2852 */
2853 end_idx = 0;
2854 startpos.lnum = current_lnum;
2855 startpos.col = startcol;
2856 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2857 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2858
2859 if (endpos.lnum == 0)
2860 {
2861 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002862 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002863 {
2864 /* a "oneline" never continues in the next line */
2865 sip->si_ends = TRUE;
2866 sip->si_m_endpos.lnum = current_lnum;
2867 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2868 }
2869 else
2870 {
2871 /* continues in the next line */
2872 sip->si_ends = FALSE;
2873 sip->si_m_endpos.lnum = 0;
2874 }
2875 sip->si_h_endpos = sip->si_m_endpos;
2876 }
2877 else
2878 {
2879 /* match within this line */
2880 sip->si_m_endpos = endpos;
2881 sip->si_h_endpos = hl_endpos;
2882 sip->si_eoe_pos = end_endpos;
2883 sip->si_ends = TRUE;
2884 sip->si_end_idx = end_idx;
2885 }
2886}
2887
2888/*
2889 * Add a new state to the current state stack.
2890 * It is cleared and the index set to "idx".
2891 * Return FAIL if it's not possible (out of memory).
2892 */
2893 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002894push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002895{
2896 if (ga_grow(&current_state, 1) == FAIL)
2897 return FAIL;
2898 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2899 CUR_STATE(current_state.ga_len).si_idx = idx;
2900 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002901 return OK;
2902}
2903
2904/*
2905 * Remove a state from the current_state stack.
2906 */
2907 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002908pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002909{
2910 if (current_state.ga_len)
2911 {
2912 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2913 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002914 }
2915 /* after the end of a pattern, try matching a keyword or pattern */
2916 next_match_idx = -1;
2917
2918 /* if first state with "keepend" is popped, reset keepend_level */
2919 if (keepend_level >= current_state.ga_len)
2920 keepend_level = -1;
2921}
2922
2923/*
2924 * Find the end of a start/skip/end syntax region after "startpos".
2925 * Only checks one line.
2926 * Also handles a match item that continued from a previous line.
2927 * If not found, the syntax item continues in the next line. m_endpos->lnum
2928 * will be 0.
2929 * If found, the end of the region and the end of the highlighting is
2930 * computed.
2931 */
2932 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002933find_endpos(
2934 int idx, /* index of the pattern */
2935 lpos_T *startpos, /* where to start looking for an END match */
2936 lpos_T *m_endpos, /* return: end of match */
2937 lpos_T *hl_endpos, /* return: end of highlighting */
2938 long *flagsp, /* return: flags of matching END */
2939 lpos_T *end_endpos, /* return: end of end pattern match */
2940 int *end_idx, /* return: group ID for end pat. match, or 0 */
2941 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002942{
2943 colnr_T matchcol;
2944 synpat_T *spp, *spp_skip;
2945 int start_idx;
2946 int best_idx;
2947 regmmatch_T regmatch;
2948 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2949 lpos_T pos;
2950 char_u *line;
2951 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002952 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002953
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002954 /* just in case we are invoked for a keyword */
2955 if (idx < 0)
2956 return;
2957
Bram Moolenaar071d4272004-06-13 20:20:40 +00002958 /*
2959 * Check for being called with a START pattern.
2960 * Can happen with a match that continues to the next line, because it
2961 * contained a region.
2962 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002963 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002964 if (spp->sp_type != SPTYPE_START)
2965 {
2966 *hl_endpos = *startpos;
2967 return;
2968 }
2969
2970 /*
2971 * Find the SKIP or first END pattern after the last START pattern.
2972 */
2973 for (;;)
2974 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002975 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002976 if (spp->sp_type != SPTYPE_START)
2977 break;
2978 ++idx;
2979 }
2980
2981 /*
2982 * Lookup the SKIP pattern (if present)
2983 */
2984 if (spp->sp_type == SPTYPE_SKIP)
2985 {
2986 spp_skip = spp;
2987 ++idx;
2988 }
2989 else
2990 spp_skip = NULL;
2991
2992 /* Setup external matches for syn_regexec(). */
2993 unref_extmatch(re_extmatch_in);
2994 re_extmatch_in = ref_extmatch(start_ext);
2995
2996 matchcol = startpos->col; /* start looking for a match at sstart */
2997 start_idx = idx; /* remember the first END pattern. */
2998 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002999
3000 /* use syntax iskeyword option */
3001 save_chartab(buf_chartab);
3002
Bram Moolenaar071d4272004-06-13 20:20:40 +00003003 for (;;)
3004 {
3005 /*
3006 * Find end pattern that matches first after "matchcol".
3007 */
3008 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003009 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003010 {
3011 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003012 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003013
Bram Moolenaar860cae12010-06-05 23:22:07 +02003014 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003015 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
3016 break;
3017 lc_col -= spp->sp_offsets[SPO_LC_OFF];
3018 if (lc_col < 0)
3019 lc_col = 0;
3020
3021 regmatch.rmm_ic = spp->sp_ic;
3022 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003023 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3024 IF_SYN_TIME(&spp->sp_time));
3025 spp->sp_prog = regmatch.regprog;
3026 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003027 {
3028 if (best_idx == -1 || regmatch.startpos[0].col
3029 < best_regmatch.startpos[0].col)
3030 {
3031 best_idx = idx;
3032 best_regmatch.startpos[0] = regmatch.startpos[0];
3033 best_regmatch.endpos[0] = regmatch.endpos[0];
3034 }
3035 }
3036 }
3037
3038 /*
3039 * If all end patterns have been tried, and there is no match, the
3040 * item continues until end-of-line.
3041 */
3042 if (best_idx == -1)
3043 break;
3044
3045 /*
3046 * If the skip pattern matches before the end pattern,
3047 * continue searching after the skip pattern.
3048 */
3049 if (spp_skip != NULL)
3050 {
3051 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003052 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003053
3054 if (lc_col < 0)
3055 lc_col = 0;
3056 regmatch.rmm_ic = spp_skip->sp_ic;
3057 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003058 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3059 IF_SYN_TIME(&spp_skip->sp_time));
3060 spp_skip->sp_prog = regmatch.regprog;
3061 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003062 <= best_regmatch.startpos[0].col)
3063 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003064 int line_len;
3065
Bram Moolenaar071d4272004-06-13 20:20:40 +00003066 /* Add offset to skip pattern match */
3067 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3068
3069 /* If the skip pattern goes on to the next line, there is no
3070 * match with an end pattern in this line. */
3071 if (pos.lnum > startpos->lnum)
3072 break;
3073
3074 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003075 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003076
3077 /* take care of an empty match or negative offset */
3078 if (pos.col <= matchcol)
3079 ++matchcol;
3080 else if (pos.col <= regmatch.endpos[0].col)
3081 matchcol = pos.col;
3082 else
3083 /* Be careful not to jump over the NUL at the end-of-line */
3084 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003085 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003086 ++matchcol)
3087 ;
3088
3089 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003090 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003091 break;
3092
3093 continue; /* start with first end pattern again */
3094 }
3095 }
3096
3097 /*
3098 * Match from start pattern to end pattern.
3099 * Correct for match and highlight offset of end pattern.
3100 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003101 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003102 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3103 /* can't end before the start */
3104 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3105 m_endpos->col = startpos->col;
3106
3107 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3108 /* can't end before the start */
3109 if (end_endpos->lnum == startpos->lnum
3110 && end_endpos->col < startpos->col)
3111 end_endpos->col = startpos->col;
3112 /* can't end after the match */
3113 limit_pos(end_endpos, m_endpos);
3114
3115 /*
3116 * If the end group is highlighted differently, adjust the pointers.
3117 */
3118 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3119 {
3120 *end_idx = best_idx;
3121 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3122 {
3123 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3124 hl_endpos->col = best_regmatch.endpos[0].col;
3125 }
3126 else
3127 {
3128 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3129 hl_endpos->col = best_regmatch.startpos[0].col;
3130 }
3131 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3132
3133 /* can't end before the start */
3134 if (hl_endpos->lnum == startpos->lnum
3135 && hl_endpos->col < startpos->col)
3136 hl_endpos->col = startpos->col;
3137 limit_pos(hl_endpos, m_endpos);
3138
3139 /* now the match ends where the highlighting ends, it is turned
3140 * into the matchgroup for the end */
3141 *m_endpos = *hl_endpos;
3142 }
3143 else
3144 {
3145 *end_idx = 0;
3146 *hl_endpos = *end_endpos;
3147 }
3148
3149 *flagsp = spp->sp_flags;
3150
3151 had_match = TRUE;
3152 break;
3153 }
3154
3155 /* no match for an END pattern in this line */
3156 if (!had_match)
3157 m_endpos->lnum = 0;
3158
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003159 restore_chartab(buf_chartab);
3160
Bram Moolenaar071d4272004-06-13 20:20:40 +00003161 /* Remove external matches. */
3162 unref_extmatch(re_extmatch_in);
3163 re_extmatch_in = NULL;
3164}
3165
3166/*
3167 * Limit "pos" not to be after "limit".
3168 */
3169 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003170limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003171{
3172 if (pos->lnum > limit->lnum)
3173 *pos = *limit;
3174 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3175 pos->col = limit->col;
3176}
3177
3178/*
3179 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3180 */
3181 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003182limit_pos_zero(
3183 lpos_T *pos,
3184 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003185{
3186 if (pos->lnum == 0)
3187 *pos = *limit;
3188 else
3189 limit_pos(pos, limit);
3190}
3191
3192/*
3193 * Add offset to matched text for end of match or highlight.
3194 */
3195 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003196syn_add_end_off(
3197 lpos_T *result, /* returned position */
3198 regmmatch_T *regmatch, /* start/end of match */
3199 synpat_T *spp, /* matched pattern */
3200 int idx, /* index of offset */
3201 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003202{
3203 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003204 int off;
3205 char_u *base;
3206 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003207
3208 if (spp->sp_off_flags & (1 << idx))
3209 {
3210 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003211 col = regmatch->startpos[0].col;
3212 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003213 }
3214 else
3215 {
3216 result->lnum = regmatch->endpos[0].lnum;
3217 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003218 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003219 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003220 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3221 * is a matchgroup. Watch out for match with last NL in the buffer. */
3222 if (result->lnum > syn_buf->b_ml.ml_line_count)
3223 col = 0;
3224 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003225 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003226 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3227 p = base + col;
3228 if (off > 0)
3229 {
3230 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003231 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003232 }
3233 else if (off < 0)
3234 {
3235 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003236 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003237 }
3238 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003239 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003240 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003241}
3242
3243/*
3244 * Add offset to matched text for start of match or highlight.
3245 * Avoid resulting column to become negative.
3246 */
3247 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003248syn_add_start_off(
3249 lpos_T *result, /* returned position */
3250 regmmatch_T *regmatch, /* start/end of match */
3251 synpat_T *spp,
3252 int idx,
3253 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003254{
3255 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003256 int off;
3257 char_u *base;
3258 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003259
3260 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3261 {
3262 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003263 col = regmatch->endpos[0].col;
3264 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003265 }
3266 else
3267 {
3268 result->lnum = regmatch->startpos[0].lnum;
3269 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003270 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003271 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003272 if (result->lnum > syn_buf->b_ml.ml_line_count)
3273 {
3274 /* a "\n" at the end of the pattern may take us below the last line */
3275 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003276 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003277 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003278 if (off != 0)
3279 {
3280 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3281 p = base + col;
3282 if (off > 0)
3283 {
3284 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003285 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003286 }
3287 else if (off < 0)
3288 {
3289 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003290 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003291 }
3292 col = (int)(p - base);
3293 }
3294 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003295}
3296
3297/*
3298 * Get current line in syntax buffer.
3299 */
3300 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003301syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003302{
3303 return ml_get_buf(syn_buf, current_lnum, FALSE);
3304}
3305
3306/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003307 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003308 * Returns TRUE when there is a match.
3309 */
3310 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003311syn_regexec(
3312 regmmatch_T *rmp,
3313 linenr_T lnum,
3314 colnr_T col,
3315 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003316{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003317 int r;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003318#ifdef FEAT_RELTIME
3319 int timed_out = FALSE;
3320#endif
Bram Moolenaarf7512552013-06-06 14:55:19 +02003321#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003322 proftime_T pt;
3323
3324 if (syn_time_on)
3325 profile_start(&pt);
3326#endif
3327
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003328 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003329 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
3330#ifdef FEAT_RELTIME
3331 syn_tm, &timed_out
3332#else
3333 NULL, NULL
3334#endif
3335 );
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003336
Bram Moolenaarf7512552013-06-06 14:55:19 +02003337#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003338 if (syn_time_on)
3339 {
3340 profile_end(&pt);
3341 profile_add(&st->total, &pt);
3342 if (profile_cmp(&pt, &st->slowest) < 0)
3343 st->slowest = pt;
3344 ++st->count;
3345 if (r > 0)
3346 ++st->match;
3347 }
3348#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003349#ifdef FEAT_RELTIME
3350 if (timed_out)
3351 syn_win->w_s->b_syn_slow = TRUE;
3352#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003353
3354 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003355 {
3356 rmp->startpos[0].lnum += lnum;
3357 rmp->endpos[0].lnum += lnum;
3358 return TRUE;
3359 }
3360 return FALSE;
3361}
3362
3363/*
3364 * Check one position in a line for a matching keyword.
3365 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003366 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003367 */
3368 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003369check_keyword_id(
3370 char_u *line,
3371 int startcol, /* position in line to check for keyword */
3372 int *endcolp, /* return: character after found keyword */
3373 long *flagsp, /* return: flags of matching keyword */
3374 short **next_listp, /* return: next_list of matching keyword */
3375 stateitem_T *cur_si, /* item at the top of the stack */
3376 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003377{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003378 keyentry_T *kp;
3379 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003380 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003381 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003382 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003383 hashtab_T *ht;
3384 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003385
3386 /* Find first character after the keyword. First character was already
3387 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003388 kwp = line + startcol;
3389 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003390 do
3391 {
3392#ifdef FEAT_MBYTE
3393 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003394 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003395 else
3396#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003397 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003398 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003399 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003400
Bram Moolenaardad6b692005-01-25 22:14:34 +00003401 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003402 return 0;
3403
3404 /*
3405 * Must make a copy of the keyword, so we can add a NUL and make it
3406 * lowercase.
3407 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003408 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003409
3410 /*
3411 * Try twice:
3412 * 1. matching case
3413 * 2. ignoring case
3414 */
3415 for (round = 1; round <= 2; ++round)
3416 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003417 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003418 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003419 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003420 if (round == 2) /* ignore case */
3421 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003422
3423 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003424 * Find keywords that match. There can be several with different
3425 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003426 * When current_next_list is non-zero accept only that group, otherwise:
3427 * Accept a not-contained keyword at toplevel.
3428 * Accept a keyword at other levels only if it is in the contains list.
3429 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003430 hi = hash_find(ht, keyword);
3431 if (!HASHITEM_EMPTY(hi))
3432 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003433 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003434 if (current_next_list != 0
3435 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3436 : (cur_si == NULL
3437 ? !(kp->flags & HL_CONTAINED)
3438 : in_id_list(cur_si, cur_si->si_cont_list,
3439 &kp->k_syn, kp->flags & HL_CONTAINED)))
3440 {
3441 *endcolp = startcol + kwlen;
3442 *flagsp = kp->flags;
3443 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003444#ifdef FEAT_CONCEAL
3445 *ccharp = kp->k_char;
3446#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003447 return kp->k_syn.id;
3448 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003449 }
3450 }
3451 return 0;
3452}
3453
3454/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003455 * Handle ":syntax conceal" command.
3456 */
3457 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003458syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003459{
3460#ifdef FEAT_CONCEAL
3461 char_u *arg = eap->arg;
3462 char_u *next;
3463
3464 eap->nextcmd = find_nextcmd(arg);
3465 if (eap->skip)
3466 return;
3467
3468 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003469 if (*arg == NUL)
3470 {
3471 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar83064062017-06-13 16:34:54 +02003472 MSG(_("syntax conceal on"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003473 else
Bram Moolenaar83064062017-06-13 16:34:54 +02003474 MSG(_("syntax conceal off"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003475 }
3476 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003477 curwin->w_s->b_syn_conceal = TRUE;
3478 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3479 curwin->w_s->b_syn_conceal = FALSE;
3480 else
3481 EMSG2(_("E390: Illegal argument: %s"), arg);
3482#endif
3483}
3484
3485/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003486 * Handle ":syntax case" command.
3487 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003488 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003489syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003490{
3491 char_u *arg = eap->arg;
3492 char_u *next;
3493
3494 eap->nextcmd = find_nextcmd(arg);
3495 if (eap->skip)
3496 return;
3497
3498 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003499 if (*arg == NUL)
3500 {
3501 if (curwin->w_s->b_syn_ic)
3502 MSG(_("syntax case ignore"));
3503 else
3504 MSG(_("syntax case match"));
3505 }
3506 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003507 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003508 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003509 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003510 else
3511 EMSG2(_("E390: Illegal argument: %s"), arg);
3512}
3513
3514/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003515 * Handle ":syntax spell" command.
3516 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003517 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003518syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003519{
3520 char_u *arg = eap->arg;
3521 char_u *next;
3522
3523 eap->nextcmd = find_nextcmd(arg);
3524 if (eap->skip)
3525 return;
3526
3527 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003528 if (*arg == NUL)
3529 {
3530 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
3531 MSG(_("syntax spell toplevel"));
3532 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
3533 MSG(_("syntax spell notoplevel"));
3534 else
3535 MSG(_("syntax spell default"));
3536 }
3537 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003538 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003539 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003540 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003541 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003542 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003543 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003544 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003545 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003546 return;
3547 }
3548
3549 /* assume spell checking changed, force a redraw */
3550 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003551}
3552
3553/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003554 * Handle ":syntax iskeyword" command.
3555 */
3556 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003557syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003558{
3559 char_u *arg = eap->arg;
3560 char_u save_chartab[32];
3561 char_u *save_isk;
3562
3563 if (eap->skip)
3564 return;
3565
3566 arg = skipwhite(arg);
3567 if (*arg == NUL)
3568 {
3569 MSG_PUTS("\n");
3570 MSG_PUTS(_("syntax iskeyword "));
3571 if (curwin->w_s->b_syn_isk != empty_option)
3572 msg_outtrans(curwin->w_s->b_syn_isk);
3573 else
3574 msg_outtrans((char_u *)"not set");
3575 }
3576 else
3577 {
3578 if (STRNICMP(arg, "clear", 5) == 0)
3579 {
3580 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3581 (size_t)32);
3582 clear_string_option(&curwin->w_s->b_syn_isk);
3583 }
3584 else
3585 {
3586 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3587 save_isk = curbuf->b_p_isk;
3588 curbuf->b_p_isk = vim_strsave(arg);
3589
3590 buf_init_chartab(curbuf, FALSE);
3591 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3592 (size_t)32);
3593 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3594 clear_string_option(&curwin->w_s->b_syn_isk);
3595 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3596 curbuf->b_p_isk = save_isk;
3597 }
3598 }
3599 redraw_win_later(curwin, NOT_VALID);
3600}
3601
3602/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003603 * Clear all syntax info for one buffer.
3604 */
3605 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003606syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003607{
3608 int i;
3609
Bram Moolenaar860cae12010-06-05 23:22:07 +02003610 block->b_syn_error = FALSE; /* clear previous error */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003611#ifdef FEAT_RELTIME
3612 block->b_syn_slow = FALSE; /* clear previous timeout */
3613#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003614 block->b_syn_ic = FALSE; /* Use case, by default */
3615 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3616 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003617#ifdef FEAT_CONCEAL
3618 block->b_syn_conceal = FALSE;
3619#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003620
3621 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003622 clear_keywtab(&block->b_keywtab);
3623 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003624
3625 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003626 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3627 syn_clear_pattern(block, i);
3628 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003629
3630 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003631 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3632 syn_clear_cluster(block, i);
3633 ga_clear(&block->b_syn_clusters);
3634 block->b_spell_cluster_id = 0;
3635 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003636
Bram Moolenaar860cae12010-06-05 23:22:07 +02003637 block->b_syn_sync_flags = 0;
3638 block->b_syn_sync_minlines = 0;
3639 block->b_syn_sync_maxlines = 0;
3640 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003641
Bram Moolenaar473de612013-06-08 18:19:48 +02003642 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003643 block->b_syn_linecont_prog = NULL;
3644 vim_free(block->b_syn_linecont_pat);
3645 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003646#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003647 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003648#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003649 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003650
3651 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003652 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003653 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003654
3655 /* Reset the counter for ":syn include" */
3656 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003657}
3658
3659/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003660 * Get rid of ownsyntax for window "wp".
3661 */
3662 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003663reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003664{
3665 if (wp->w_s != &wp->w_buffer->b_s)
3666 {
3667 syntax_clear(wp->w_s);
3668 vim_free(wp->w_s);
3669 wp->w_s = &wp->w_buffer->b_s;
3670 }
3671}
3672
3673/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003674 * Clear syncing info for one buffer.
3675 */
3676 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003677syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003678{
3679 int i;
3680
3681 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003682 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3683 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3684 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003685
Bram Moolenaar860cae12010-06-05 23:22:07 +02003686 curwin->w_s->b_syn_sync_flags = 0;
3687 curwin->w_s->b_syn_sync_minlines = 0;
3688 curwin->w_s->b_syn_sync_maxlines = 0;
3689 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003690
Bram Moolenaar473de612013-06-08 18:19:48 +02003691 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003692 curwin->w_s->b_syn_linecont_prog = NULL;
3693 vim_free(curwin->w_s->b_syn_linecont_pat);
3694 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003695 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003696
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003697 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003698}
3699
3700/*
3701 * Remove one pattern from the buffer's pattern list.
3702 */
3703 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003704syn_remove_pattern(
3705 synblock_T *block,
3706 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003707{
3708 synpat_T *spp;
3709
Bram Moolenaar860cae12010-06-05 23:22:07 +02003710 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003711#ifdef FEAT_FOLDING
3712 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003713 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003714#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003715 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003716 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003717 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3718 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003719}
3720
3721/*
3722 * Clear and free one syntax pattern. When clearing all, must be called from
3723 * last to first!
3724 */
3725 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003726syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003727{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003728 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003729 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003730 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003731 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003732 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003733 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3734 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3735 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003736 }
3737}
3738
3739/*
3740 * Clear and free one syntax cluster.
3741 */
3742 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003743syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003744{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003745 vim_free(SYN_CLSTR(block)[i].scl_name);
3746 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3747 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003748}
3749
3750/*
3751 * Handle ":syntax clear" command.
3752 */
3753 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003754syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003755{
3756 char_u *arg = eap->arg;
3757 char_u *arg_end;
3758 int id;
3759
3760 eap->nextcmd = find_nextcmd(arg);
3761 if (eap->skip)
3762 return;
3763
3764 /*
3765 * We have to disable this within ":syn include @group filename",
3766 * because otherwise @group would get deleted.
3767 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3768 * clear".
3769 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003770 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003771 return;
3772
3773 if (ends_excmd(*arg))
3774 {
3775 /*
3776 * No argument: Clear all syntax items.
3777 */
3778 if (syncing)
3779 syntax_sync_clear();
3780 else
3781 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003782 syntax_clear(curwin->w_s);
3783 if (curwin->w_s == &curwin->w_buffer->b_s)
3784 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003785 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003786 }
3787 }
3788 else
3789 {
3790 /*
3791 * Clear the group IDs that are in the argument.
3792 */
3793 while (!ends_excmd(*arg))
3794 {
3795 arg_end = skiptowhite(arg);
3796 if (*arg == '@')
3797 {
3798 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3799 if (id == 0)
3800 {
3801 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3802 break;
3803 }
3804 else
3805 {
3806 /*
3807 * We can't physically delete a cluster without changing
3808 * the IDs of other clusters, so we do the next best thing
3809 * and make it empty.
3810 */
3811 short scl_id = id - SYNID_CLUSTER;
3812
Bram Moolenaar860cae12010-06-05 23:22:07 +02003813 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3814 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003815 }
3816 }
3817 else
3818 {
3819 id = syn_namen2id(arg, (int)(arg_end - arg));
3820 if (id == 0)
3821 {
3822 EMSG2(_(e_nogroup), arg);
3823 break;
3824 }
3825 else
3826 syn_clear_one(id, syncing);
3827 }
3828 arg = skipwhite(arg_end);
3829 }
3830 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003831 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003832 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003833}
3834
3835/*
3836 * Clear one syntax group for the current buffer.
3837 */
3838 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003839syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003840{
3841 synpat_T *spp;
3842 int idx;
3843
3844 /* Clear keywords only when not ":syn sync clear group-name" */
3845 if (!syncing)
3846 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003847 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3848 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003849 }
3850
3851 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003852 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003853 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003854 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003855 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3856 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003857 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003858 }
3859}
3860
3861/*
3862 * Handle ":syntax on" command.
3863 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003864 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003865syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003866{
3867 syn_cmd_onoff(eap, "syntax");
3868}
3869
3870/*
3871 * Handle ":syntax enable" command.
3872 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003873 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003874syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003875{
3876 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3877 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003878 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003879}
3880
3881/*
3882 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003883 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003884 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003885 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003886syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003887{
3888 eap->nextcmd = check_nextcmd(eap->arg);
3889 if (!eap->skip)
3890 {
3891 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3892 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003893 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003894 }
3895}
3896
3897/*
3898 * Handle ":syntax manual" command.
3899 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003900 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003901syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003902{
3903 syn_cmd_onoff(eap, "manual");
3904}
3905
3906/*
3907 * Handle ":syntax off" command.
3908 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003909 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003910syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003911{
3912 syn_cmd_onoff(eap, "nosyntax");
3913}
3914
3915 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003916syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003917{
3918 char_u buf[100];
3919
3920 eap->nextcmd = check_nextcmd(eap->arg);
3921 if (!eap->skip)
3922 {
3923 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003924 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003925 do_cmdline_cmd(buf);
3926 }
3927}
3928
3929/*
3930 * Handle ":syntax [list]" command: list current syntax words.
3931 */
3932 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003933syn_cmd_list(
3934 exarg_T *eap,
3935 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003936{
3937 char_u *arg = eap->arg;
3938 int id;
3939 char_u *arg_end;
3940
3941 eap->nextcmd = find_nextcmd(arg);
3942 if (eap->skip)
3943 return;
3944
Bram Moolenaar860cae12010-06-05 23:22:07 +02003945 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003946 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003947 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003948 return;
3949 }
3950
3951 if (syncing)
3952 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003953 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003954 {
3955 MSG_PUTS(_("syncing on C-style comments"));
3956 syn_lines_msg();
3957 syn_match_msg();
3958 return;
3959 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003960 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003961 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003962 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003963 MSG_PUTS(_("no syncing"));
3964 else
3965 {
3966 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003967 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003968 MSG_PUTS(_(" lines before top line"));
3969 syn_match_msg();
3970 }
3971 return;
3972 }
3973 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003974 if (curwin->w_s->b_syn_sync_minlines > 0
3975 || curwin->w_s->b_syn_sync_maxlines > 0
3976 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003977 {
3978 MSG_PUTS(_("\nsyncing on items"));
3979 syn_lines_msg();
3980 syn_match_msg();
3981 }
3982 }
3983 else
3984 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3985 if (ends_excmd(*arg))
3986 {
3987 /*
3988 * No argument: List all group IDs and all syntax clusters.
3989 */
3990 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3991 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003992 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003993 syn_list_cluster(id);
3994 }
3995 else
3996 {
3997 /*
3998 * List the group IDs and syntax clusters that are in the argument.
3999 */
4000 while (!ends_excmd(*arg) && !got_int)
4001 {
4002 arg_end = skiptowhite(arg);
4003 if (*arg == '@')
4004 {
4005 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
4006 if (id == 0)
4007 EMSG2(_("E392: No such syntax cluster: %s"), arg);
4008 else
4009 syn_list_cluster(id - SYNID_CLUSTER);
4010 }
4011 else
4012 {
4013 id = syn_namen2id(arg, (int)(arg_end - arg));
4014 if (id == 0)
4015 EMSG2(_(e_nogroup), arg);
4016 else
4017 syn_list_one(id, syncing, TRUE);
4018 }
4019 arg = skipwhite(arg_end);
4020 }
4021 }
4022 eap->nextcmd = check_nextcmd(arg);
4023}
4024
4025 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004026syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004027{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004028 if (curwin->w_s->b_syn_sync_maxlines > 0
4029 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004030 {
4031 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02004032 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004033 {
4034 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004035 msg_outnum(curwin->w_s->b_syn_sync_minlines);
4036 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004037 MSG_PUTS(", ");
4038 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004039 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004040 {
4041 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004042 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004043 }
4044 MSG_PUTS(_(" lines before top line"));
4045 }
4046}
4047
4048 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004049syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004050{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004051 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004052 {
4053 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004054 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004055 MSG_PUTS(_(" line breaks"));
4056 }
4057}
4058
4059static int last_matchgroup;
4060
4061struct name_list
4062{
4063 int flag;
4064 char *name;
4065};
4066
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004067static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004068
4069/*
4070 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4071 */
4072 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004073syn_list_one(
4074 int id,
4075 int syncing, /* when TRUE: list syncing items */
4076 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004077{
4078 int attr;
4079 int idx;
4080 int did_header = FALSE;
4081 synpat_T *spp;
4082 static struct name_list namelist1[] =
4083 {
4084 {HL_DISPLAY, "display"},
4085 {HL_CONTAINED, "contained"},
4086 {HL_ONELINE, "oneline"},
4087 {HL_KEEPEND, "keepend"},
4088 {HL_EXTEND, "extend"},
4089 {HL_EXCLUDENL, "excludenl"},
4090 {HL_TRANSP, "transparent"},
4091 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004092#ifdef FEAT_CONCEAL
4093 {HL_CONCEAL, "conceal"},
4094 {HL_CONCEALENDS, "concealends"},
4095#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004096 {0, NULL}
4097 };
4098 static struct name_list namelist2[] =
4099 {
4100 {HL_SKIPWHITE, "skipwhite"},
4101 {HL_SKIPNL, "skipnl"},
4102 {HL_SKIPEMPTY, "skipempty"},
4103 {0, NULL}
4104 };
4105
Bram Moolenaar8820b482017-03-16 17:23:31 +01004106 attr = HL_ATTR(HLF_D); /* highlight like directories */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004107
4108 /* list the keywords for "id" */
4109 if (!syncing)
4110 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004111 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4112 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004113 did_header, attr);
4114 }
4115
4116 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004117 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004118 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004119 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004120 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4121 continue;
4122
4123 (void)syn_list_header(did_header, 999, id);
4124 did_header = TRUE;
4125 last_matchgroup = 0;
4126 if (spp->sp_type == SPTYPE_MATCH)
4127 {
4128 put_pattern("match", ' ', spp, attr);
4129 msg_putchar(' ');
4130 }
4131 else if (spp->sp_type == SPTYPE_START)
4132 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004133 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4134 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4135 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4136 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4137 while (idx < curwin->w_s->b_syn_patterns.ga_len
4138 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4139 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004140 --idx;
4141 msg_putchar(' ');
4142 }
4143 syn_list_flags(namelist1, spp->sp_flags, attr);
4144
4145 if (spp->sp_cont_list != NULL)
4146 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4147
4148 if (spp->sp_syn.cont_in_list != NULL)
4149 put_id_list((char_u *)"containedin",
4150 spp->sp_syn.cont_in_list, attr);
4151
4152 if (spp->sp_next_list != NULL)
4153 {
4154 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4155 syn_list_flags(namelist2, spp->sp_flags, attr);
4156 }
4157 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4158 {
4159 if (spp->sp_flags & HL_SYNC_HERE)
4160 msg_puts_attr((char_u *)"grouphere", attr);
4161 else
4162 msg_puts_attr((char_u *)"groupthere", attr);
4163 msg_putchar(' ');
4164 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004165 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004166 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4167 else
4168 MSG_PUTS("NONE");
4169 msg_putchar(' ');
4170 }
4171 }
4172
4173 /* list the link, if there is one */
4174 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4175 {
4176 (void)syn_list_header(did_header, 999, id);
4177 msg_puts_attr((char_u *)"links to", attr);
4178 msg_putchar(' ');
4179 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4180 }
4181}
4182
4183 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004184syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004185{
4186 int i;
4187
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004188 for (i = 0; nlist[i].flag != 0; ++i)
4189 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004190 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004191 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004192 msg_putchar(' ');
4193 }
4194}
4195
4196/*
4197 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4198 */
4199 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004200syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004201{
4202 int endcol = 15;
4203
4204 /* slight hack: roughly duplicate the guts of syn_list_header() */
4205 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004206 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004207
4208 if (msg_col >= endcol) /* output at least one space */
4209 endcol = msg_col + 1;
4210 if (Columns <= endcol) /* avoid hang for tiny window */
4211 endcol = Columns - 1;
4212
4213 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004214 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004215 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004216 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004217 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004218 }
4219 else
4220 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01004221 msg_puts_attr((char_u *)"cluster", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004222 msg_puts((char_u *)"=NONE");
4223 }
4224}
4225
4226 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004227put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004228{
4229 short *p;
4230
4231 msg_puts_attr(name, attr);
4232 msg_putchar('=');
4233 for (p = list; *p; ++p)
4234 {
4235 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4236 {
4237 if (p[1])
4238 MSG_PUTS("ALLBUT");
4239 else
4240 MSG_PUTS("ALL");
4241 }
4242 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4243 {
4244 MSG_PUTS("TOP");
4245 }
4246 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4247 {
4248 MSG_PUTS("CONTAINED");
4249 }
4250 else if (*p >= SYNID_CLUSTER)
4251 {
4252 short scl_id = *p - SYNID_CLUSTER;
4253
4254 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004255 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004256 }
4257 else
4258 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4259 if (p[1])
4260 msg_putchar(',');
4261 }
4262 msg_putchar(' ');
4263}
4264
4265 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004266put_pattern(
4267 char *s,
4268 int c,
4269 synpat_T *spp,
4270 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004271{
4272 long n;
4273 int mask;
4274 int first;
4275 static char *sepchars = "/+=-#@\"|'^&";
4276 int i;
4277
4278 /* May have to write "matchgroup=group" */
4279 if (last_matchgroup != spp->sp_syn_match_id)
4280 {
4281 last_matchgroup = spp->sp_syn_match_id;
4282 msg_puts_attr((char_u *)"matchgroup", attr);
4283 msg_putchar('=');
4284 if (last_matchgroup == 0)
4285 msg_outtrans((char_u *)"NONE");
4286 else
4287 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4288 msg_putchar(' ');
4289 }
4290
4291 /* output the name of the pattern and an '=' or ' ' */
4292 msg_puts_attr((char_u *)s, attr);
4293 msg_putchar(c);
4294
4295 /* output the pattern, in between a char that is not in the pattern */
4296 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4297 if (sepchars[++i] == NUL)
4298 {
4299 i = 0; /* no good char found, just use the first one */
4300 break;
4301 }
4302 msg_putchar(sepchars[i]);
4303 msg_outtrans(spp->sp_pattern);
4304 msg_putchar(sepchars[i]);
4305
4306 /* output any pattern options */
4307 first = TRUE;
4308 for (i = 0; i < SPO_COUNT; ++i)
4309 {
4310 mask = (1 << i);
4311 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4312 {
4313 if (!first)
4314 msg_putchar(','); /* separate with commas */
4315 msg_puts((char_u *)spo_name_tab[i]);
4316 n = spp->sp_offsets[i];
4317 if (i != SPO_LC_OFF)
4318 {
4319 if (spp->sp_off_flags & mask)
4320 msg_putchar('s');
4321 else
4322 msg_putchar('e');
4323 if (n > 0)
4324 msg_putchar('+');
4325 }
4326 if (n || i == SPO_LC_OFF)
4327 msg_outnum(n);
4328 first = FALSE;
4329 }
4330 }
4331 msg_putchar(' ');
4332}
4333
4334/*
4335 * List or clear the keywords for one syntax group.
4336 * Return TRUE if the header has been printed.
4337 */
4338 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004339syn_list_keywords(
4340 int id,
4341 hashtab_T *ht,
4342 int did_header, /* header has already been printed */
4343 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004344{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004345 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004346 hashitem_T *hi;
4347 keyentry_T *kp;
4348 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004349 int prev_contained = 0;
4350 short *prev_next_list = NULL;
4351 short *prev_cont_in_list = NULL;
4352 int prev_skipnl = 0;
4353 int prev_skipwhite = 0;
4354 int prev_skipempty = 0;
4355
Bram Moolenaar071d4272004-06-13 20:20:40 +00004356 /*
4357 * Unfortunately, this list of keywords is not sorted on alphabet but on
4358 * hash value...
4359 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004360 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004361 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004362 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004363 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004364 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004365 --todo;
4366 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004367 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004368 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004369 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004370 if (prev_contained != (kp->flags & HL_CONTAINED)
4371 || prev_skipnl != (kp->flags & HL_SKIPNL)
4372 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4373 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4374 || prev_cont_in_list != kp->k_syn.cont_in_list
4375 || prev_next_list != kp->next_list)
4376 outlen = 9999;
4377 else
4378 outlen = (int)STRLEN(kp->keyword);
4379 /* output "contained" and "nextgroup" on each line */
4380 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004381 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004382 prev_contained = 0;
4383 prev_next_list = NULL;
4384 prev_cont_in_list = NULL;
4385 prev_skipnl = 0;
4386 prev_skipwhite = 0;
4387 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004388 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004389 did_header = TRUE;
4390 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004391 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004392 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004393 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004394 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004395 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004396 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004397 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004398 put_id_list((char_u *)"containedin",
4399 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004400 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004401 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004402 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004403 if (kp->next_list != prev_next_list)
4404 {
4405 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4406 msg_putchar(' ');
4407 prev_next_list = kp->next_list;
4408 if (kp->flags & HL_SKIPNL)
4409 {
4410 msg_puts_attr((char_u *)"skipnl", attr);
4411 msg_putchar(' ');
4412 prev_skipnl = (kp->flags & HL_SKIPNL);
4413 }
4414 if (kp->flags & HL_SKIPWHITE)
4415 {
4416 msg_puts_attr((char_u *)"skipwhite", attr);
4417 msg_putchar(' ');
4418 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4419 }
4420 if (kp->flags & HL_SKIPEMPTY)
4421 {
4422 msg_puts_attr((char_u *)"skipempty", attr);
4423 msg_putchar(' ');
4424 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4425 }
4426 }
4427 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004428 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004429 }
4430 }
4431 }
4432
4433 return did_header;
4434}
4435
4436 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004437syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004438{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004439 hashitem_T *hi;
4440 keyentry_T *kp;
4441 keyentry_T *kp_prev;
4442 keyentry_T *kp_next;
4443 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004444
Bram Moolenaardad6b692005-01-25 22:14:34 +00004445 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004446 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004447 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004448 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004449 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004450 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004451 --todo;
4452 kp_prev = NULL;
4453 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004454 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004455 if (kp->k_syn.id == id)
4456 {
4457 kp_next = kp->ke_next;
4458 if (kp_prev == NULL)
4459 {
4460 if (kp_next == NULL)
4461 hash_remove(ht, hi);
4462 else
4463 hi->hi_key = KE2HIKEY(kp_next);
4464 }
4465 else
4466 kp_prev->ke_next = kp_next;
4467 vim_free(kp->next_list);
4468 vim_free(kp->k_syn.cont_in_list);
4469 vim_free(kp);
4470 kp = kp_next;
4471 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004472 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004473 {
4474 kp_prev = kp;
4475 kp = kp->ke_next;
4476 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004477 }
4478 }
4479 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004480 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004481}
4482
4483/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004484 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004485 */
4486 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004487clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004488{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004489 hashitem_T *hi;
4490 int todo;
4491 keyentry_T *kp;
4492 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004493
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004494 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004495 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004496 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004497 if (!HASHITEM_EMPTY(hi))
4498 {
4499 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004500 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004501 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004502 kp_next = kp->ke_next;
4503 vim_free(kp->next_list);
4504 vim_free(kp->k_syn.cont_in_list);
4505 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004506 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004507 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004508 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004509 hash_clear(ht);
4510 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004511}
4512
4513/*
4514 * Add a keyword to the list of keywords.
4515 */
4516 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004517add_keyword(
4518 char_u *name, /* name of keyword */
4519 int id, /* group ID for this keyword */
4520 int flags, /* flags for this keyword */
4521 short *cont_in_list, /* containedin for this keyword */
4522 short *next_list, /* nextgroup for this keyword */
4523 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004524{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004525 keyentry_T *kp;
4526 hashtab_T *ht;
4527 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004528 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004529 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004530 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004531
Bram Moolenaar860cae12010-06-05 23:22:07 +02004532 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004533 name_ic = str_foldcase(name, (int)STRLEN(name),
4534 name_folded, MAXKEYWLEN + 1);
4535 else
4536 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004537 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4538 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004539 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004540 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004541 kp->k_syn.id = id;
4542 kp->k_syn.inc_tag = current_syn_inc_tag;
4543 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004544 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004545 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004546 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004547 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004548 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004549
Bram Moolenaar860cae12010-06-05 23:22:07 +02004550 if (curwin->w_s->b_syn_ic)
4551 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004552 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004553 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004554
Bram Moolenaardad6b692005-01-25 22:14:34 +00004555 hash = hash_hash(kp->keyword);
4556 hi = hash_lookup(ht, kp->keyword, hash);
4557 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004558 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004559 /* new keyword, add to hashtable */
4560 kp->ke_next = NULL;
4561 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004562 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004563 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004564 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004565 /* keyword already exists, prepend to list */
4566 kp->ke_next = HI2KE(hi);
4567 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004568 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004569}
4570
4571/*
4572 * Get the start and end of the group name argument.
4573 * Return a pointer to the first argument.
4574 * Return NULL if the end of the command was found instead of further args.
4575 */
4576 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004577get_group_name(
4578 char_u *arg, /* start of the argument */
4579 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004580{
4581 char_u *rest;
4582
4583 *name_end = skiptowhite(arg);
4584 rest = skipwhite(*name_end);
4585
4586 /*
4587 * Check if there are enough arguments. The first argument may be a
4588 * pattern, where '|' is allowed, so only check for NUL.
4589 */
4590 if (ends_excmd(*arg) || *rest == NUL)
4591 return NULL;
4592 return rest;
4593}
4594
4595/*
4596 * Check for syntax command option arguments.
4597 * This can be called at any place in the list of arguments, and just picks
4598 * out the arguments that are known. Can be called several times in a row to
4599 * collect all options in between other arguments.
4600 * Return a pointer to the next argument (which isn't an option).
4601 * Return NULL for any error;
4602 */
4603 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004604get_syn_options(
4605 char_u *arg, /* next argument to be checked */
4606 syn_opt_arg_T *opt, /* various things */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004607 int *conceal_char UNUSED,
4608 int skip) /* TRUE if skipping over command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004609{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004610 char_u *gname_start, *gname;
4611 int syn_id;
4612 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004613 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004614 int i;
4615 int fidx;
4616 static struct flag
4617 {
4618 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004619 int argtype;
4620 int flags;
4621 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4622 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4623 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4624 {"eExXtTeEnNdD", 0, HL_EXTEND},
4625 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4626 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4627 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4628 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4629 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4630 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4631 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4632 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4633 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004634 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4635 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4636 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004637 {"cCoOnNtTaAiInNsS", 1, 0},
4638 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4639 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004640 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004641 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004642
4643 if (arg == NULL) /* already detected error */
4644 return NULL;
4645
Bram Moolenaar860cae12010-06-05 23:22:07 +02004646#ifdef FEAT_CONCEAL
4647 if (curwin->w_s->b_syn_conceal)
4648 opt->flags |= HL_CONCEAL;
4649#endif
4650
Bram Moolenaar071d4272004-06-13 20:20:40 +00004651 for (;;)
4652 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004653 /*
4654 * This is used very often when a large number of keywords is defined.
4655 * Need to skip quickly when no option name is found.
4656 * Also avoid tolower(), it's slow.
4657 */
4658 if (strchr(first_letters, *arg) == NULL)
4659 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004660
4661 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4662 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004663 p = flagtab[fidx].name;
4664 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4665 if (arg[len] != p[i] && arg[len] != p[i + 1])
4666 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004667 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004668 || (flagtab[fidx].argtype > 0
4669 ? arg[len] == '='
4670 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004671 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004672 if (opt->keyword
4673 && (flagtab[fidx].flags == HL_DISPLAY
4674 || flagtab[fidx].flags == HL_FOLD
4675 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004676 /* treat "display", "fold" and "extend" as a keyword */
4677 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004678 break;
4679 }
4680 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004681 if (fidx < 0) /* no match found */
4682 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004683
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004684 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004685 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004686 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004687 {
4688 EMSG(_("E395: contains argument not accepted here"));
4689 return NULL;
4690 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004691 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004692 return NULL;
4693 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004694 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004695 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004696 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004697 return NULL;
4698 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004699 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004700 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004701 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004702 return NULL;
4703 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004704 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4705 {
4706#ifdef FEAT_MBYTE
4707 /* cchar=? */
4708 if (has_mbyte)
4709 {
4710# ifdef FEAT_CONCEAL
4711 *conceal_char = mb_ptr2char(arg + 6);
4712# endif
4713 arg += mb_ptr2len(arg + 6) - 1;
4714 }
4715 else
4716#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004717 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004718#ifdef FEAT_CONCEAL
4719 *conceal_char = arg[6];
4720#else
4721 ;
4722#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004723 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004724#ifdef FEAT_CONCEAL
4725 if (!vim_isprintc_strict(*conceal_char))
4726 {
4727 EMSG(_("E844: invalid cchar value"));
4728 return NULL;
4729 }
4730#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004731 arg = skipwhite(arg + 7);
4732 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004733 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004734 {
4735 opt->flags |= flagtab[fidx].flags;
4736 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004737
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004738 if (flagtab[fidx].flags == HL_SYNC_HERE
4739 || flagtab[fidx].flags == HL_SYNC_THERE)
4740 {
4741 if (opt->sync_idx == NULL)
4742 {
4743 EMSG(_("E393: group[t]here not accepted here"));
4744 return NULL;
4745 }
4746 gname_start = arg;
4747 arg = skiptowhite(arg);
4748 if (gname_start == arg)
4749 return NULL;
4750 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4751 if (gname == NULL)
4752 return NULL;
4753 if (STRCMP(gname, "NONE") == 0)
4754 *opt->sync_idx = NONE_IDX;
4755 else
4756 {
4757 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004758 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4759 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4760 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004761 {
4762 *opt->sync_idx = i;
4763 break;
4764 }
4765 if (i < 0)
4766 {
4767 EMSG2(_("E394: Didn't find region item for %s"), gname);
4768 vim_free(gname);
4769 return NULL;
4770 }
4771 }
4772
4773 vim_free(gname);
4774 arg = skipwhite(arg);
4775 }
4776#ifdef FEAT_FOLDING
4777 else if (flagtab[fidx].flags == HL_FOLD
4778 && foldmethodIsSyntax(curwin))
4779 /* Need to update folds later. */
4780 foldUpdateAll(curwin);
4781#endif
4782 }
4783 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004784
4785 return arg;
4786}
4787
4788/*
4789 * Adjustments to syntax item when declared in a ":syn include"'d file.
4790 * Set the contained flag, and if the item is not already contained, add it
4791 * to the specified top-level group, if any.
4792 */
4793 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004794syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004795{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004796 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004797 return;
4798 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004799 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004800 {
4801 /* We have to alloc this, because syn_combine_list() will free it. */
4802 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004803 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004804
4805 if (grp_list != NULL)
4806 {
4807 grp_list[0] = id;
4808 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004809 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004810 CLUSTER_ADD);
4811 }
4812 }
4813}
4814
4815/*
4816 * Handle ":syntax include [@{group-name}] filename" command.
4817 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004818 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004819syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004820{
4821 char_u *arg = eap->arg;
4822 int sgl_id = 1;
4823 char_u *group_name_end;
4824 char_u *rest;
4825 char_u *errormsg = NULL;
4826 int prev_toplvl_grp;
4827 int prev_syn_inc_tag;
4828 int source = FALSE;
4829
4830 eap->nextcmd = find_nextcmd(arg);
4831 if (eap->skip)
4832 return;
4833
4834 if (arg[0] == '@')
4835 {
4836 ++arg;
4837 rest = get_group_name(arg, &group_name_end);
4838 if (rest == NULL)
4839 {
4840 EMSG((char_u *)_("E397: Filename required"));
4841 return;
4842 }
4843 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004844 if (sgl_id == 0)
4845 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004846 /* separate_nextcmd() and expand_filename() depend on this */
4847 eap->arg = rest;
4848 }
4849
4850 /*
4851 * Everything that's left, up to the next command, should be the
4852 * filename to include.
4853 */
4854 eap->argt |= (XFILE | NOSPC);
4855 separate_nextcmd(eap);
4856 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4857 {
4858 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4859 * file. Need to expand the file name first. In other cases
4860 * ":runtime!" is used. */
4861 source = TRUE;
4862 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4863 {
4864 if (errormsg != NULL)
4865 EMSG(errormsg);
4866 return;
4867 }
4868 }
4869
4870 /*
4871 * Save and restore the existing top-level grouplist id and ":syn
4872 * include" tag around the actual inclusion.
4873 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004874 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4875 {
4876 EMSG((char_u *)_("E847: Too many syntax includes"));
4877 return;
4878 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004879 prev_syn_inc_tag = current_syn_inc_tag;
4880 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004881 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4882 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004883 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004884 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004885 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004886 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004887 current_syn_inc_tag = prev_syn_inc_tag;
4888}
4889
4890/*
4891 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4892 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004893 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004894syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004895{
4896 char_u *arg = eap->arg;
4897 char_u *group_name_end;
4898 int syn_id;
4899 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004900 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004901 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004902 char_u *kw;
4903 syn_opt_arg_T syn_opt_arg;
4904 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004905 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004906
4907 rest = get_group_name(arg, &group_name_end);
4908
4909 if (rest != NULL)
4910 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004911 if (eap->skip)
4912 syn_id = -1;
4913 else
4914 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004915 if (syn_id != 0)
4916 /* allocate a buffer, for removing backslashes in the keyword */
4917 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004918 if (keyword_copy != NULL)
4919 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004920 syn_opt_arg.flags = 0;
4921 syn_opt_arg.keyword = TRUE;
4922 syn_opt_arg.sync_idx = NULL;
4923 syn_opt_arg.has_cont_list = FALSE;
4924 syn_opt_arg.cont_in_list = NULL;
4925 syn_opt_arg.next_list = NULL;
4926
Bram Moolenaar071d4272004-06-13 20:20:40 +00004927 /*
4928 * The options given apply to ALL keywords, so all options must be
4929 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004930 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004931 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004932 cnt = 0;
4933 p = keyword_copy;
4934 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004935 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004936 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4937 eap->skip);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004938 if (rest == NULL || ends_excmd(*rest))
4939 break;
4940 /* Copy the keyword, removing backslashes, and add a NUL. */
Bram Moolenaar1c465442017-03-12 20:10:05 +01004941 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004942 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004943 if (*rest == '\\' && rest[1] != NUL)
4944 ++rest;
4945 *p++ = *rest++;
4946 }
4947 *p++ = NUL;
4948 ++cnt;
4949 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004950
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004951 if (!eap->skip)
4952 {
4953 /* Adjust flags for use of ":syn include". */
4954 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4955
4956 /*
4957 * 2: Add an entry for each keyword.
4958 */
4959 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4960 {
4961 for (p = vim_strchr(kw, '['); ; )
4962 {
4963 if (p != NULL)
4964 *p = NUL;
4965 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004966 syn_opt_arg.cont_in_list,
4967 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004968 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004969 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004970 if (p[1] == NUL)
4971 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004972 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004973 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004974 }
4975 if (p[1] == ']')
4976 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004977 if (p[2] != NUL)
4978 {
4979 EMSG3(_("E890: trailing char after ']': %s]%s"),
4980 kw, &p[2]);
4981 goto error;
4982 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004983 kw = p + 1; /* skip over the "]" */
4984 break;
4985 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004986#ifdef FEAT_MBYTE
4987 if (has_mbyte)
4988 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004989 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004990
4991 mch_memmove(p, p + 1, l);
4992 p += l;
4993 }
4994 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004995#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004996 {
4997 p[0] = p[1];
4998 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004999 }
5000 }
5001 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005002 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02005003error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005004 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00005005 vim_free(syn_opt_arg.cont_in_list);
5006 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005007 }
5008 }
5009
5010 if (rest != NULL)
5011 eap->nextcmd = check_nextcmd(rest);
5012 else
5013 EMSG2(_(e_invarg2), arg);
5014
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005015 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005016 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005017}
5018
5019/*
5020 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
5021 *
5022 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
5023 */
5024 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005025syn_cmd_match(
5026 exarg_T *eap,
5027 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005028{
5029 char_u *arg = eap->arg;
5030 char_u *group_name_end;
5031 char_u *rest;
5032 synpat_T item; /* the item found in the line */
5033 int syn_id;
5034 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005035 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005036 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005037 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005038
5039 /* Isolate the group name, check for validity */
5040 rest = get_group_name(arg, &group_name_end);
5041
5042 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005043 syn_opt_arg.flags = 0;
5044 syn_opt_arg.keyword = FALSE;
5045 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
5046 syn_opt_arg.has_cont_list = TRUE;
5047 syn_opt_arg.cont_list = NULL;
5048 syn_opt_arg.cont_in_list = NULL;
5049 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005050 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005051
5052 /* get the pattern. */
5053 init_syn_patterns();
5054 vim_memset(&item, 0, sizeof(item));
5055 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005056 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
5057 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005058
5059 /* Get options after the pattern */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005060 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005061
5062 if (rest != NULL) /* all arguments are valid */
5063 {
5064 /*
5065 * Check for trailing command and illegal trailing arguments.
5066 */
5067 eap->nextcmd = check_nextcmd(rest);
5068 if (!ends_excmd(*rest) || eap->skip)
5069 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005070 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005071 && (syn_id = syn_check_group(arg,
5072 (int)(group_name_end - arg))) != 0)
5073 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005074 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005075 /*
5076 * Store the pattern in the syn_items list
5077 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005078 idx = curwin->w_s->b_syn_patterns.ga_len;
5079 SYN_ITEMS(curwin->w_s)[idx] = item;
5080 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5081 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5082 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5083 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5084 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5085 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5086 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5087 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005088 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005089#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005090 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005091#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005092 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005093 curwin->w_s->b_syn_containedin = TRUE;
5094 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5095 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005096
5097 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005098 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005099 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005100#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005101 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005102 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005103#endif
5104
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005105 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005106 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005107 return; /* don't free the progs and patterns now */
5108 }
5109 }
5110
5111 /*
5112 * Something failed, free the allocated memory.
5113 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005114 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005115 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005116 vim_free(syn_opt_arg.cont_list);
5117 vim_free(syn_opt_arg.cont_in_list);
5118 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005119
5120 if (rest == NULL)
5121 EMSG2(_(e_invarg2), arg);
5122}
5123
5124/*
5125 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5126 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5127 */
5128 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005129syn_cmd_region(
5130 exarg_T *eap,
5131 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005132{
5133 char_u *arg = eap->arg;
5134 char_u *group_name_end;
5135 char_u *rest; /* next arg, NULL on error */
5136 char_u *key_end;
5137 char_u *key = NULL;
5138 char_u *p;
5139 int item;
5140#define ITEM_START 0
5141#define ITEM_SKIP 1
5142#define ITEM_END 2
5143#define ITEM_MATCHGROUP 3
5144 struct pat_ptr
5145 {
5146 synpat_T *pp_synp; /* pointer to syn_pattern */
5147 int pp_matchgroup_id; /* matchgroup ID */
5148 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5149 } *(pat_ptrs[3]);
5150 /* patterns found in the line */
5151 struct pat_ptr *ppp;
5152 struct pat_ptr *ppp_next;
5153 int pat_count = 0; /* nr of syn_patterns found */
5154 int syn_id;
5155 int matchgroup_id = 0;
5156 int not_enough = FALSE; /* not enough arguments */
5157 int illegal = FALSE; /* illegal arguments */
5158 int success = FALSE;
5159 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005160 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005161 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005162
5163 /* Isolate the group name, check for validity */
5164 rest = get_group_name(arg, &group_name_end);
5165
5166 pat_ptrs[0] = NULL;
5167 pat_ptrs[1] = NULL;
5168 pat_ptrs[2] = NULL;
5169
5170 init_syn_patterns();
5171
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005172 syn_opt_arg.flags = 0;
5173 syn_opt_arg.keyword = FALSE;
5174 syn_opt_arg.sync_idx = NULL;
5175 syn_opt_arg.has_cont_list = TRUE;
5176 syn_opt_arg.cont_list = NULL;
5177 syn_opt_arg.cont_in_list = NULL;
5178 syn_opt_arg.next_list = NULL;
5179
Bram Moolenaar071d4272004-06-13 20:20:40 +00005180 /*
5181 * get the options, patterns and matchgroup.
5182 */
5183 while (rest != NULL && !ends_excmd(*rest))
5184 {
5185 /* Check for option arguments */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005186 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005187 if (rest == NULL || ends_excmd(*rest))
5188 break;
5189
5190 /* must be a pattern or matchgroup then */
5191 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005192 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005193 ++key_end;
5194 vim_free(key);
5195 key = vim_strnsave_up(rest, (int)(key_end - rest));
5196 if (key == NULL) /* out of memory */
5197 {
5198 rest = NULL;
5199 break;
5200 }
5201 if (STRCMP(key, "MATCHGROUP") == 0)
5202 item = ITEM_MATCHGROUP;
5203 else if (STRCMP(key, "START") == 0)
5204 item = ITEM_START;
5205 else if (STRCMP(key, "END") == 0)
5206 item = ITEM_END;
5207 else if (STRCMP(key, "SKIP") == 0)
5208 {
5209 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5210 {
5211 illegal = TRUE;
5212 break;
5213 }
5214 item = ITEM_SKIP;
5215 }
5216 else
5217 break;
5218 rest = skipwhite(key_end);
5219 if (*rest != '=')
5220 {
5221 rest = NULL;
5222 EMSG2(_("E398: Missing '=': %s"), arg);
5223 break;
5224 }
5225 rest = skipwhite(rest + 1);
5226 if (*rest == NUL)
5227 {
5228 not_enough = TRUE;
5229 break;
5230 }
5231
5232 if (item == ITEM_MATCHGROUP)
5233 {
5234 p = skiptowhite(rest);
5235 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5236 matchgroup_id = 0;
5237 else
5238 {
5239 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5240 if (matchgroup_id == 0)
5241 {
5242 illegal = TRUE;
5243 break;
5244 }
5245 }
5246 rest = skipwhite(p);
5247 }
5248 else
5249 {
5250 /*
5251 * Allocate room for a syn_pattern, and link it in the list of
5252 * syn_patterns for this item, at the start (because the list is
5253 * used from end to start).
5254 */
5255 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5256 if (ppp == NULL)
5257 {
5258 rest = NULL;
5259 break;
5260 }
5261 ppp->pp_next = pat_ptrs[item];
5262 pat_ptrs[item] = ppp;
5263 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5264 if (ppp->pp_synp == NULL)
5265 {
5266 rest = NULL;
5267 break;
5268 }
5269
5270 /*
5271 * Get the syntax pattern and the following offset(s).
5272 */
5273 /* Enable the appropriate \z specials. */
5274 if (item == ITEM_START)
5275 reg_do_extmatch = REX_SET;
5276 else if (item == ITEM_SKIP || item == ITEM_END)
5277 reg_do_extmatch = REX_USE;
5278 rest = get_syn_pattern(rest, ppp->pp_synp);
5279 reg_do_extmatch = 0;
5280 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005281 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005282 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5283 ppp->pp_matchgroup_id = matchgroup_id;
5284 ++pat_count;
5285 }
5286 }
5287 vim_free(key);
5288 if (illegal || not_enough)
5289 rest = NULL;
5290
5291 /*
5292 * Must have a "start" and "end" pattern.
5293 */
5294 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5295 pat_ptrs[ITEM_END] == NULL))
5296 {
5297 not_enough = TRUE;
5298 rest = NULL;
5299 }
5300
5301 if (rest != NULL)
5302 {
5303 /*
5304 * Check for trailing garbage or command.
5305 * If OK, add the item.
5306 */
5307 eap->nextcmd = check_nextcmd(rest);
5308 if (!ends_excmd(*rest) || eap->skip)
5309 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005310 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005311 && (syn_id = syn_check_group(arg,
5312 (int)(group_name_end - arg))) != 0)
5313 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005314 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005315 /*
5316 * Store the start/skip/end in the syn_items list
5317 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005318 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005319 for (item = ITEM_START; item <= ITEM_END; ++item)
5320 {
5321 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5322 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005323 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5324 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5325 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005326 (item == ITEM_START) ? SPTYPE_START :
5327 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005328 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5329 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005330 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5331 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005332 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005333 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005334#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005335 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005336#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005337 if (item == ITEM_START)
5338 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005339 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005340 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005341 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005342 syn_opt_arg.cont_in_list;
5343 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005344 curwin->w_s->b_syn_containedin = TRUE;
5345 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005346 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005347 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005348 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005349 ++idx;
5350#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005351 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005352 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005353#endif
5354 }
5355 }
5356
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005357 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005358 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005359 success = TRUE; /* don't free the progs and patterns now */
5360 }
5361 }
5362
5363 /*
5364 * Free the allocated memory.
5365 */
5366 for (item = ITEM_START; item <= ITEM_END; ++item)
5367 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5368 {
5369 if (!success)
5370 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005371 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005372 vim_free(ppp->pp_synp->sp_pattern);
5373 }
5374 vim_free(ppp->pp_synp);
5375 ppp_next = ppp->pp_next;
5376 vim_free(ppp);
5377 }
5378
5379 if (!success)
5380 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005381 vim_free(syn_opt_arg.cont_list);
5382 vim_free(syn_opt_arg.cont_in_list);
5383 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005384 if (not_enough)
5385 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5386 else if (illegal || rest == NULL)
5387 EMSG2(_(e_invarg2), arg);
5388 }
5389}
5390
5391/*
5392 * A simple syntax group ID comparison function suitable for use in qsort()
5393 */
5394 static int
5395#ifdef __BORLANDC__
5396_RTLENTRYF
5397#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005398syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005399{
5400 const short *s1 = v1;
5401 const short *s2 = v2;
5402
5403 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5404}
5405
5406/*
5407 * Combines lists of syntax clusters.
5408 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5409 */
5410 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005411syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005412{
5413 int count1 = 0;
5414 int count2 = 0;
5415 short *g1;
5416 short *g2;
5417 short *clstr = NULL;
5418 int count;
5419 int round;
5420
5421 /*
5422 * Handle degenerate cases.
5423 */
5424 if (*clstr2 == NULL)
5425 return;
5426 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5427 {
5428 if (list_op == CLUSTER_REPLACE)
5429 vim_free(*clstr1);
5430 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5431 *clstr1 = *clstr2;
5432 else
5433 vim_free(*clstr2);
5434 return;
5435 }
5436
5437 for (g1 = *clstr1; *g1; g1++)
5438 ++count1;
5439 for (g2 = *clstr2; *g2; g2++)
5440 ++count2;
5441
5442 /*
5443 * For speed purposes, sort both lists.
5444 */
5445 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5446 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5447
5448 /*
5449 * We proceed in two passes; in round 1, we count the elements to place
5450 * in the new list, and in round 2, we allocate and populate the new
5451 * list. For speed, we use a mergesort-like method, adding the smaller
5452 * of the current elements in each list to the new list.
5453 */
5454 for (round = 1; round <= 2; round++)
5455 {
5456 g1 = *clstr1;
5457 g2 = *clstr2;
5458 count = 0;
5459
5460 /*
5461 * First, loop through the lists until one of them is empty.
5462 */
5463 while (*g1 && *g2)
5464 {
5465 /*
5466 * We always want to add from the first list.
5467 */
5468 if (*g1 < *g2)
5469 {
5470 if (round == 2)
5471 clstr[count] = *g1;
5472 count++;
5473 g1++;
5474 continue;
5475 }
5476 /*
5477 * We only want to add from the second list if we're adding the
5478 * lists.
5479 */
5480 if (list_op == CLUSTER_ADD)
5481 {
5482 if (round == 2)
5483 clstr[count] = *g2;
5484 count++;
5485 }
5486 if (*g1 == *g2)
5487 g1++;
5488 g2++;
5489 }
5490
5491 /*
5492 * Now add the leftovers from whichever list didn't get finished
5493 * first. As before, we only want to add from the second list if
5494 * we're adding the lists.
5495 */
5496 for (; *g1; g1++, count++)
5497 if (round == 2)
5498 clstr[count] = *g1;
5499 if (list_op == CLUSTER_ADD)
5500 for (; *g2; g2++, count++)
5501 if (round == 2)
5502 clstr[count] = *g2;
5503
5504 if (round == 1)
5505 {
5506 /*
5507 * If the group ended up empty, we don't need to allocate any
5508 * space for it.
5509 */
5510 if (count == 0)
5511 {
5512 clstr = NULL;
5513 break;
5514 }
5515 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5516 if (clstr == NULL)
5517 break;
5518 clstr[count] = 0;
5519 }
5520 }
5521
5522 /*
5523 * Finally, put the new list in place.
5524 */
5525 vim_free(*clstr1);
5526 vim_free(*clstr2);
5527 *clstr1 = clstr;
5528}
5529
5530/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005531 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005532 * If it is not found, 0 is returned.
5533 */
5534 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005535syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005536{
5537 int i;
5538 char_u *name_u;
5539
5540 /* Avoid using stricmp() too much, it's slow on some systems */
5541 name_u = vim_strsave_up(name);
5542 if (name_u == NULL)
5543 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005544 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5545 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5546 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005547 break;
5548 vim_free(name_u);
5549 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5550}
5551
5552/*
5553 * Like syn_scl_name2id(), but take a pointer + length argument.
5554 */
5555 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005556syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005557{
5558 char_u *name;
5559 int id = 0;
5560
5561 name = vim_strnsave(linep, len);
5562 if (name != NULL)
5563 {
5564 id = syn_scl_name2id(name);
5565 vim_free(name);
5566 }
5567 return id;
5568}
5569
5570/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005571 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005572 * The argument is a pointer to the name and the length of the name.
5573 * If it doesn't exist yet, a new entry is created.
5574 * Return 0 for failure.
5575 */
5576 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005577syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005578{
5579 int id;
5580 char_u *name;
5581
5582 name = vim_strnsave(pp, len);
5583 if (name == NULL)
5584 return 0;
5585
5586 id = syn_scl_name2id(name);
5587 if (id == 0) /* doesn't exist yet */
5588 id = syn_add_cluster(name);
5589 else
5590 vim_free(name);
5591 return id;
5592}
5593
5594/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005595 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005596 * "name" must be an allocated string, it will be consumed.
5597 * Return 0 for failure.
5598 */
5599 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005600syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005601{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005602 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005603
5604 /*
5605 * First call for this growarray: init growing array.
5606 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005607 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005608 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005609 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5610 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005611 }
5612
Bram Moolenaar42431a72011-04-01 14:44:59 +02005613 len = curwin->w_s->b_syn_clusters.ga_len;
5614 if (len >= MAX_CLUSTER_ID)
5615 {
5616 EMSG((char_u *)_("E848: Too many syntax clusters"));
5617 vim_free(name);
5618 return 0;
5619 }
5620
Bram Moolenaar071d4272004-06-13 20:20:40 +00005621 /*
5622 * Make room for at least one other cluster entry.
5623 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005624 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005625 {
5626 vim_free(name);
5627 return 0;
5628 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005629
Bram Moolenaar860cae12010-06-05 23:22:07 +02005630 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5631 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5632 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5633 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5634 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005635
Bram Moolenaar217ad922005-03-20 22:37:15 +00005636 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005637 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005638 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005639 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005640
Bram Moolenaar071d4272004-06-13 20:20:40 +00005641 return len + SYNID_CLUSTER;
5642}
5643
5644/*
5645 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5646 * [add={groupname},..] [remove={groupname},..]".
5647 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005648 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005649syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005650{
5651 char_u *arg = eap->arg;
5652 char_u *group_name_end;
5653 char_u *rest;
5654 int scl_id;
5655 short *clstr_list;
5656 int got_clstr = FALSE;
5657 int opt_len;
5658 int list_op;
5659
5660 eap->nextcmd = find_nextcmd(arg);
5661 if (eap->skip)
5662 return;
5663
5664 rest = get_group_name(arg, &group_name_end);
5665
5666 if (rest != NULL)
5667 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005668 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5669 if (scl_id == 0)
5670 return;
5671 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005672
5673 for (;;)
5674 {
5675 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005676 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005677 {
5678 opt_len = 3;
5679 list_op = CLUSTER_ADD;
5680 }
5681 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005682 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005683 {
5684 opt_len = 6;
5685 list_op = CLUSTER_SUBTRACT;
5686 }
5687 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005688 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005689 {
5690 opt_len = 8;
5691 list_op = CLUSTER_REPLACE;
5692 }
5693 else
5694 break;
5695
5696 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005697 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005698 {
5699 EMSG2(_(e_invarg2), rest);
5700 break;
5701 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005702 if (scl_id >= 0)
5703 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005704 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005705 else
5706 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005707 got_clstr = TRUE;
5708 }
5709
5710 if (got_clstr)
5711 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005712 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005713 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005714 }
5715 }
5716
5717 if (!got_clstr)
5718 EMSG(_("E400: No cluster specified"));
5719 if (rest == NULL || !ends_excmd(*rest))
5720 EMSG2(_(e_invarg2), arg);
5721}
5722
5723/*
5724 * On first call for current buffer: Init growing array.
5725 */
5726 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005727init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005728{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005729 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5730 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005731}
5732
5733/*
5734 * Get one pattern for a ":syntax match" or ":syntax region" command.
5735 * Stores the pattern and program in a synpat_T.
5736 * Returns a pointer to the next argument, or NULL in case of an error.
5737 */
5738 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005739get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005740{
5741 char_u *end;
5742 int *p;
5743 int idx;
5744 char_u *cpo_save;
5745
5746 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005747 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005748 return NULL;
5749
5750 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5751 if (*end != *arg) /* end delimiter not found */
5752 {
5753 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5754 return NULL;
5755 }
5756 /* store the pattern and compiled regexp program */
5757 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5758 return NULL;
5759
5760 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5761 cpo_save = p_cpo;
5762 p_cpo = (char_u *)"";
5763 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5764 p_cpo = cpo_save;
5765
5766 if (ci->sp_prog == NULL)
5767 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005768 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005769#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005770 syn_clear_time(&ci->sp_time);
5771#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005772
5773 /*
5774 * Check for a match, highlight or region offset.
5775 */
5776 ++end;
5777 do
5778 {
5779 for (idx = SPO_COUNT; --idx >= 0; )
5780 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5781 break;
5782 if (idx >= 0)
5783 {
5784 p = &(ci->sp_offsets[idx]);
5785 if (idx != SPO_LC_OFF)
5786 switch (end[3])
5787 {
5788 case 's': break;
5789 case 'b': break;
5790 case 'e': idx += SPO_COUNT; break;
5791 default: idx = -1; break;
5792 }
5793 if (idx >= 0)
5794 {
5795 ci->sp_off_flags |= (1 << idx);
5796 if (idx == SPO_LC_OFF) /* lc=99 */
5797 {
5798 end += 3;
5799 *p = getdigits(&end);
5800
5801 /* "lc=" offset automatically sets "ms=" offset */
5802 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5803 {
5804 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5805 ci->sp_offsets[SPO_MS_OFF] = *p;
5806 }
5807 }
5808 else /* yy=x+99 */
5809 {
5810 end += 4;
5811 if (*end == '+')
5812 {
5813 ++end;
5814 *p = getdigits(&end); /* positive offset */
5815 }
5816 else if (*end == '-')
5817 {
5818 ++end;
5819 *p = -getdigits(&end); /* negative offset */
5820 }
5821 }
5822 if (*end != ',')
5823 break;
5824 ++end;
5825 }
5826 }
5827 } while (idx >= 0);
5828
Bram Moolenaar1c465442017-03-12 20:10:05 +01005829 if (!ends_excmd(*end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005830 {
5831 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5832 return NULL;
5833 }
5834 return skipwhite(end);
5835}
5836
5837/*
5838 * Handle ":syntax sync .." command.
5839 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005840 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005841syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005842{
5843 char_u *arg_start = eap->arg;
5844 char_u *arg_end;
5845 char_u *key = NULL;
5846 char_u *next_arg;
5847 int illegal = FALSE;
5848 int finished = FALSE;
5849 long n;
5850 char_u *cpo_save;
5851
5852 if (ends_excmd(*arg_start))
5853 {
5854 syn_cmd_list(eap, TRUE);
5855 return;
5856 }
5857
5858 while (!ends_excmd(*arg_start))
5859 {
5860 arg_end = skiptowhite(arg_start);
5861 next_arg = skipwhite(arg_end);
5862 vim_free(key);
5863 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5864 if (STRCMP(key, "CCOMMENT") == 0)
5865 {
5866 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005867 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005868 if (!ends_excmd(*next_arg))
5869 {
5870 arg_end = skiptowhite(next_arg);
5871 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005872 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005873 (int)(arg_end - next_arg));
5874 next_arg = skipwhite(arg_end);
5875 }
5876 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005877 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005878 }
5879 else if ( STRNCMP(key, "LINES", 5) == 0
5880 || STRNCMP(key, "MINLINES", 8) == 0
5881 || STRNCMP(key, "MAXLINES", 8) == 0
5882 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5883 {
5884 if (key[4] == 'S')
5885 arg_end = key + 6;
5886 else if (key[0] == 'L')
5887 arg_end = key + 11;
5888 else
5889 arg_end = key + 9;
5890 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5891 {
5892 illegal = TRUE;
5893 break;
5894 }
5895 n = getdigits(&arg_end);
5896 if (!eap->skip)
5897 {
5898 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005899 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005900 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005901 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005902 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005903 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005904 }
5905 }
5906 else if (STRCMP(key, "FROMSTART") == 0)
5907 {
5908 if (!eap->skip)
5909 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005910 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5911 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005912 }
5913 }
5914 else if (STRCMP(key, "LINECONT") == 0)
5915 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005916 if (*next_arg == NUL) /* missing pattern */
5917 {
5918 illegal = TRUE;
5919 break;
5920 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005921 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005922 {
5923 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5924 finished = TRUE;
5925 break;
5926 }
5927 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5928 if (*arg_end != *next_arg) /* end delimiter not found */
5929 {
5930 illegal = TRUE;
5931 break;
5932 }
5933
5934 if (!eap->skip)
5935 {
5936 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005937 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005938 (int)(arg_end - next_arg - 1))) == NULL)
5939 {
5940 finished = TRUE;
5941 break;
5942 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005943 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005944
5945 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5946 cpo_save = p_cpo;
5947 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005948 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005949 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005950 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005951#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005952 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5953#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005954
Bram Moolenaar860cae12010-06-05 23:22:07 +02005955 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005956 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005957 vim_free(curwin->w_s->b_syn_linecont_pat);
5958 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005959 finished = TRUE;
5960 break;
5961 }
5962 }
5963 next_arg = skipwhite(arg_end + 1);
5964 }
5965 else
5966 {
5967 eap->arg = next_arg;
5968 if (STRCMP(key, "MATCH") == 0)
5969 syn_cmd_match(eap, TRUE);
5970 else if (STRCMP(key, "REGION") == 0)
5971 syn_cmd_region(eap, TRUE);
5972 else if (STRCMP(key, "CLEAR") == 0)
5973 syn_cmd_clear(eap, TRUE);
5974 else
5975 illegal = TRUE;
5976 finished = TRUE;
5977 break;
5978 }
5979 arg_start = next_arg;
5980 }
5981 vim_free(key);
5982 if (illegal)
5983 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5984 else if (!finished)
5985 {
5986 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005987 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005988 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005989 }
5990}
5991
5992/*
5993 * Convert a line of highlight group names into a list of group ID numbers.
5994 * "arg" should point to the "contains" or "nextgroup" keyword.
5995 * "arg" is advanced to after the last group name.
5996 * Careful: the argument is modified (NULs added).
5997 * returns FAIL for some error, OK for success.
5998 */
5999 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006000get_id_list(
6001 char_u **arg,
6002 int keylen, /* length of keyword */
Bram Moolenaarde318c52017-01-17 16:27:10 +01006003 short **list, /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00006004 NULL, the list is silently skipped! */
Bram Moolenaarde318c52017-01-17 16:27:10 +01006005 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006006{
6007 char_u *p = NULL;
6008 char_u *end;
6009 int round;
6010 int count;
6011 int total_count = 0;
6012 short *retval = NULL;
6013 char_u *name;
6014 regmatch_T regmatch;
6015 int id;
6016 int i;
6017 int failed = FALSE;
6018
6019 /*
6020 * We parse the list twice:
6021 * round == 1: count the number of items, allocate the array.
6022 * round == 2: fill the array with the items.
6023 * In round 1 new groups may be added, causing the number of items to
6024 * grow when a regexp is used. In that case round 1 is done once again.
6025 */
6026 for (round = 1; round <= 2; ++round)
6027 {
6028 /*
6029 * skip "contains"
6030 */
6031 p = skipwhite(*arg + keylen);
6032 if (*p != '=')
6033 {
6034 EMSG2(_("E405: Missing equal sign: %s"), *arg);
6035 break;
6036 }
6037 p = skipwhite(p + 1);
6038 if (ends_excmd(*p))
6039 {
6040 EMSG2(_("E406: Empty argument: %s"), *arg);
6041 break;
6042 }
6043
6044 /*
6045 * parse the arguments after "contains"
6046 */
6047 count = 0;
6048 while (!ends_excmd(*p))
6049 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01006050 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006051 ;
6052 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
6053 if (name == NULL)
6054 {
6055 failed = TRUE;
6056 break;
6057 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006058 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006059 if ( STRCMP(name + 1, "ALLBUT") == 0
6060 || STRCMP(name + 1, "ALL") == 0
6061 || STRCMP(name + 1, "TOP") == 0
6062 || STRCMP(name + 1, "CONTAINED") == 0)
6063 {
6064 if (TOUPPER_ASC(**arg) != 'C')
6065 {
6066 EMSG2(_("E407: %s not allowed here"), name + 1);
6067 failed = TRUE;
6068 vim_free(name);
6069 break;
6070 }
6071 if (count != 0)
6072 {
Bram Moolenaard7a96152017-01-22 15:28:55 +01006073 EMSG2(_("E408: %s must be first in contains list"),
6074 name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006075 failed = TRUE;
6076 vim_free(name);
6077 break;
6078 }
6079 if (name[1] == 'A')
6080 id = SYNID_ALLBUT;
6081 else if (name[1] == 'T')
6082 id = SYNID_TOP;
6083 else
6084 id = SYNID_CONTAINED;
6085 id += current_syn_inc_tag;
6086 }
6087 else if (name[1] == '@')
6088 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01006089 if (skip)
6090 id = -1;
6091 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01006092 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006093 }
6094 else
6095 {
6096 /*
6097 * Handle full group name.
6098 */
6099 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6100 id = syn_check_group(name + 1, (int)(end - p));
6101 else
6102 {
6103 /*
6104 * Handle match of regexp with group names.
6105 */
6106 *name = '^';
6107 STRCAT(name, "$");
6108 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6109 if (regmatch.regprog == NULL)
6110 {
6111 failed = TRUE;
6112 vim_free(name);
6113 break;
6114 }
6115
6116 regmatch.rm_ic = TRUE;
6117 id = 0;
6118 for (i = highlight_ga.ga_len; --i >= 0; )
6119 {
6120 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6121 (colnr_T)0))
6122 {
6123 if (round == 2)
6124 {
6125 /* Got more items than expected; can happen
6126 * when adding items that match:
6127 * "contains=a.*b,axb".
6128 * Go back to first round */
6129 if (count >= total_count)
6130 {
6131 vim_free(retval);
6132 round = 1;
6133 }
6134 else
6135 retval[count] = i + 1;
6136 }
6137 ++count;
6138 id = -1; /* remember that we found one */
6139 }
6140 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006141 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006142 }
6143 }
6144 vim_free(name);
6145 if (id == 0)
6146 {
6147 EMSG2(_("E409: Unknown group name: %s"), p);
6148 failed = TRUE;
6149 break;
6150 }
6151 if (id > 0)
6152 {
6153 if (round == 2)
6154 {
6155 /* Got more items than expected, go back to first round */
6156 if (count >= total_count)
6157 {
6158 vim_free(retval);
6159 round = 1;
6160 }
6161 else
6162 retval[count] = id;
6163 }
6164 ++count;
6165 }
6166 p = skipwhite(end);
6167 if (*p != ',')
6168 break;
6169 p = skipwhite(p + 1); /* skip comma in between arguments */
6170 }
6171 if (failed)
6172 break;
6173 if (round == 1)
6174 {
6175 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6176 if (retval == NULL)
6177 break;
6178 retval[count] = 0; /* zero means end of the list */
6179 total_count = count;
6180 }
6181 }
6182
6183 *arg = p;
6184 if (failed || retval == NULL)
6185 {
6186 vim_free(retval);
6187 return FAIL;
6188 }
6189
6190 if (*list == NULL)
6191 *list = retval;
6192 else
6193 vim_free(retval); /* list already found, don't overwrite it */
6194
6195 return OK;
6196}
6197
6198/*
6199 * Make a copy of an ID list.
6200 */
6201 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006202copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006203{
6204 int len;
6205 int count;
6206 short *retval;
6207
6208 if (list == NULL)
6209 return NULL;
6210
6211 for (count = 0; list[count]; ++count)
6212 ;
6213 len = (count + 1) * sizeof(short);
6214 retval = (short *)alloc((unsigned)len);
6215 if (retval != NULL)
6216 mch_memmove(retval, list, (size_t)len);
6217
6218 return retval;
6219}
6220
6221/*
6222 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6223 * "cur_si" can be NULL if not checking the "containedin" list.
6224 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6225 * the current item.
6226 * This function is called very often, keep it fast!!
6227 */
6228 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006229in_id_list(
6230 stateitem_T *cur_si, /* current item or NULL */
6231 short *list, /* id list */
6232 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6233 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006234{
6235 int retval;
6236 short *scl_list;
6237 short item;
6238 short id = ssp->id;
6239 static int depth = 0;
6240 int r;
6241
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006242 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006243 if (cur_si != NULL && ssp->cont_in_list != NULL
6244 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006245 {
6246 /* Ignore transparent items without a contains argument. Double check
6247 * that we don't go back past the first one. */
6248 while ((cur_si->si_flags & HL_TRANS_CONT)
6249 && cur_si > (stateitem_T *)(current_state.ga_data))
6250 --cur_si;
6251 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6252 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006253 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6254 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006255 return TRUE;
6256 }
6257
6258 if (list == NULL)
6259 return FALSE;
6260
6261 /*
6262 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6263 * inside anything. Only allow not-contained groups.
6264 */
6265 if (list == ID_LIST_ALL)
6266 return !contained;
6267
6268 /*
6269 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6270 * contains list. We also require that "id" is at the same ":syn include"
6271 * level as the list.
6272 */
6273 item = *list;
6274 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6275 {
6276 if (item < SYNID_TOP)
6277 {
6278 /* ALL or ALLBUT: accept all groups in the same file */
6279 if (item - SYNID_ALLBUT != ssp->inc_tag)
6280 return FALSE;
6281 }
6282 else if (item < SYNID_CONTAINED)
6283 {
6284 /* TOP: accept all not-contained groups in the same file */
6285 if (item - SYNID_TOP != ssp->inc_tag || contained)
6286 return FALSE;
6287 }
6288 else
6289 {
6290 /* CONTAINED: accept all contained groups in the same file */
6291 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6292 return FALSE;
6293 }
6294 item = *++list;
6295 retval = FALSE;
6296 }
6297 else
6298 retval = TRUE;
6299
6300 /*
6301 * Return "retval" if id is in the contains list.
6302 */
6303 while (item != 0)
6304 {
6305 if (item == id)
6306 return retval;
6307 if (item >= SYNID_CLUSTER)
6308 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006309 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006310 /* restrict recursiveness to 30 to avoid an endless loop for a
6311 * cluster that includes itself (indirectly) */
6312 if (scl_list != NULL && depth < 30)
6313 {
6314 ++depth;
6315 r = in_id_list(NULL, scl_list, ssp, contained);
6316 --depth;
6317 if (r)
6318 return retval;
6319 }
6320 }
6321 item = *++list;
6322 }
6323 return !retval;
6324}
6325
6326struct subcommand
6327{
Bram Moolenaard99df422016-01-29 23:20:40 +01006328 char *name; /* subcommand name */
6329 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006330};
6331
6332static struct subcommand subcommands[] =
6333{
6334 {"case", syn_cmd_case},
6335 {"clear", syn_cmd_clear},
6336 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006337 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006338 {"enable", syn_cmd_enable},
6339 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006340 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006341 {"keyword", syn_cmd_keyword},
6342 {"list", syn_cmd_list},
6343 {"manual", syn_cmd_manual},
6344 {"match", syn_cmd_match},
6345 {"on", syn_cmd_on},
6346 {"off", syn_cmd_off},
6347 {"region", syn_cmd_region},
6348 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006349 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006350 {"sync", syn_cmd_sync},
6351 {"", syn_cmd_list},
6352 {NULL, NULL}
6353};
6354
6355/*
6356 * ":syntax".
6357 * This searches the subcommands[] table for the subcommand name, and calls a
6358 * syntax_subcommand() function to do the rest.
6359 */
6360 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006361ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006362{
6363 char_u *arg = eap->arg;
6364 char_u *subcmd_end;
6365 char_u *subcmd_name;
6366 int i;
6367
6368 syn_cmdlinep = eap->cmdlinep;
6369
6370 /* isolate subcommand name */
6371 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6372 ;
6373 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6374 if (subcmd_name != NULL)
6375 {
6376 if (eap->skip) /* skip error messages for all subcommands */
6377 ++emsg_skip;
6378 for (i = 0; ; ++i)
6379 {
6380 if (subcommands[i].name == NULL)
6381 {
6382 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6383 break;
6384 }
6385 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6386 {
6387 eap->arg = skipwhite(subcmd_end);
6388 (subcommands[i].func)(eap, FALSE);
6389 break;
6390 }
6391 }
6392 vim_free(subcmd_name);
6393 if (eap->skip)
6394 --emsg_skip;
6395 }
6396}
6397
Bram Moolenaar860cae12010-06-05 23:22:07 +02006398 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006399ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006400{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006401 char_u *old_value;
6402 char_u *new_value;
6403
Bram Moolenaar860cae12010-06-05 23:22:07 +02006404 if (curwin->w_s == &curwin->w_buffer->b_s)
6405 {
6406 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6407 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006408 hash_init(&curwin->w_s->b_keywtab);
6409 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006410#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006411 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006412 curwin->w_p_spell = FALSE; /* No spell checking */
6413 clear_string_option(&curwin->w_s->b_p_spc);
6414 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006415 clear_string_option(&curwin->w_s->b_p_spl);
6416#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006417 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006418 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006419
6420 /* save value of b:current_syntax */
6421 old_value = get_var_value((char_u *)"b:current_syntax");
6422 if (old_value != NULL)
6423 old_value = vim_strsave(old_value);
6424
Bram Moolenaard1413d92016-03-02 21:51:56 +01006425#ifdef FEAT_AUTOCMD
Bram Moolenaar1950c352010-06-06 15:21:10 +02006426 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6427 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006428 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaard1413d92016-03-02 21:51:56 +01006429#endif
Bram Moolenaar1950c352010-06-06 15:21:10 +02006430
6431 /* move value of b:current_syntax to w:current_syntax */
6432 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006433 if (new_value != NULL)
6434 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006435
6436 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006437 if (old_value == NULL)
6438 do_unlet((char_u *)"b:current_syntax", TRUE);
6439 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006440 {
6441 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6442 vim_free(old_value);
6443 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006444}
6445
6446 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006447syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006448{
6449 return (win->w_s->b_syn_patterns.ga_len != 0
6450 || win->w_s->b_syn_clusters.ga_len != 0
6451 || win->w_s->b_keywtab.ht_used > 0
6452 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006453}
6454
6455#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6456
6457static enum
6458{
6459 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006460 EXP_CASE, /* expand ":syn case" arguments */
6461 EXP_SPELL, /* expand ":syn spell" arguments */
6462 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006463} expand_what;
6464
Bram Moolenaar4f688582007-07-24 12:34:30 +00006465/*
6466 * Reset include_link, include_default, include_none to 0.
6467 * Called when we are done expanding.
6468 */
6469 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006470reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006471{
6472 include_link = include_default = include_none = 0;
6473}
6474
6475/*
6476 * Handle command line completion for :match and :echohl command: Add "None"
6477 * as highlight group.
6478 */
6479 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006480set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006481{
6482 xp->xp_context = EXPAND_HIGHLIGHT;
6483 xp->xp_pattern = arg;
6484 include_none = 1;
6485}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006486
6487/*
6488 * Handle command line completion for :syntax command.
6489 */
6490 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006491set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006492{
6493 char_u *p;
6494
6495 /* Default: expand subcommands */
6496 xp->xp_context = EXPAND_SYNTAX;
6497 expand_what = EXP_SUBCMD;
6498 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006499 include_link = 0;
6500 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006501
6502 /* (part of) subcommand already typed */
6503 if (*arg != NUL)
6504 {
6505 p = skiptowhite(arg);
6506 if (*p != NUL) /* past first word */
6507 {
6508 xp->xp_pattern = skipwhite(p);
6509 if (*skiptowhite(xp->xp_pattern) != NUL)
6510 xp->xp_context = EXPAND_NOTHING;
6511 else if (STRNICMP(arg, "case", p - arg) == 0)
6512 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006513 else if (STRNICMP(arg, "spell", p - arg) == 0)
6514 expand_what = EXP_SPELL;
6515 else if (STRNICMP(arg, "sync", p - arg) == 0)
6516 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006517 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6518 || STRNICMP(arg, "region", p - arg) == 0
6519 || STRNICMP(arg, "match", p - arg) == 0
6520 || STRNICMP(arg, "list", p - arg) == 0)
6521 xp->xp_context = EXPAND_HIGHLIGHT;
6522 else
6523 xp->xp_context = EXPAND_NOTHING;
6524 }
6525 }
6526}
6527
Bram Moolenaar071d4272004-06-13 20:20:40 +00006528/*
6529 * Function given to ExpandGeneric() to obtain the list syntax names for
6530 * expansion.
6531 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006532 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006533get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006534{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006535 switch (expand_what)
6536 {
6537 case EXP_SUBCMD:
6538 return (char_u *)subcommands[idx].name;
6539 case EXP_CASE:
6540 {
6541 static char *case_args[] = {"match", "ignore", NULL};
6542 return (char_u *)case_args[idx];
6543 }
6544 case EXP_SPELL:
6545 {
6546 static char *spell_args[] =
6547 {"toplevel", "notoplevel", "default", NULL};
6548 return (char_u *)spell_args[idx];
6549 }
6550 case EXP_SYNC:
6551 {
6552 static char *sync_args[] =
6553 {"ccomment", "clear", "fromstart",
6554 "linebreaks=", "linecont", "lines=", "match",
6555 "maxlines=", "minlines=", "region", NULL};
6556 return (char_u *)sync_args[idx];
6557 }
6558 }
6559 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006560}
6561
6562#endif /* FEAT_CMDL_COMPL */
6563
Bram Moolenaar071d4272004-06-13 20:20:40 +00006564/*
6565 * Function called for expression evaluation: get syntax ID at file position.
6566 */
6567 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006568syn_get_id(
6569 win_T *wp,
6570 long lnum,
6571 colnr_T col,
6572 int trans, /* remove transparency */
6573 int *spellp, /* return: can do spell checking */
6574 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006575{
6576 /* When the position is not after the current position and in the same
6577 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006578 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006579 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006580 || col < current_col)
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006581 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006582 else if (wp->w_buffer == syn_buf
6583 && lnum == current_lnum
6584 && col > current_col)
6585 /* next_match may not be correct when moving around, e.g. with the
6586 * "skip" expression in searchpair() */
6587 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006588
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006589 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006590
6591 return (trans ? current_trans_id : current_id);
6592}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006593
Bram Moolenaar860cae12010-06-05 23:22:07 +02006594#if defined(FEAT_CONCEAL) || defined(PROTO)
6595/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006596 * Get extra information about the syntax item. Must be called right after
6597 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006598 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006599 * Returns the current flags.
6600 */
6601 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006602get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006603{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006604 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006605 return current_flags;
6606}
6607
6608/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006609 * Return conceal substitution character
6610 */
6611 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006612syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006613{
6614 return current_sub_char;
6615}
6616#endif
6617
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006618#if defined(FEAT_EVAL) || defined(PROTO)
6619/*
6620 * Return the syntax ID at position "i" in the current stack.
6621 * The caller must have called syn_get_id() before to fill the stack.
6622 * Returns -1 when "i" is out of range.
6623 */
6624 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006625syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006626{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006627 if (i >= current_state.ga_len)
6628 {
6629 /* Need to invalidate the state, because we didn't properly finish it
6630 * for the last character, "keep_state" was TRUE. */
6631 invalidate_current_state();
6632 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006633 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006634 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006635 return CUR_STATE(i).si_id;
6636}
6637#endif
6638
Bram Moolenaar071d4272004-06-13 20:20:40 +00006639#if defined(FEAT_FOLDING) || defined(PROTO)
6640/*
6641 * Function called to get folding level for line "lnum" in window "wp".
6642 */
6643 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006644syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006645{
6646 int level = 0;
6647 int i;
6648
6649 /* Return quickly when there are no fold items at all. */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006650 if (wp->w_s->b_syn_folditems != 0
6651 && !wp->w_s->b_syn_error
6652# ifdef SYN_TIME_LIMIT
6653 && !wp->w_s->b_syn_slow
6654# endif
6655 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006656 {
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006657 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006658
6659 for (i = 0; i < current_state.ga_len; ++i)
6660 if (CUR_STATE(i).si_flags & HL_FOLD)
6661 ++level;
6662 }
6663 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006664 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006665 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006666 if (level < 0)
6667 level = 0;
6668 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006669 return level;
6670}
6671#endif
6672
Bram Moolenaar01615492015-02-03 13:00:38 +01006673#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006674/*
6675 * ":syntime".
6676 */
6677 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006678ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006679{
6680 if (STRCMP(eap->arg, "on") == 0)
6681 syn_time_on = TRUE;
6682 else if (STRCMP(eap->arg, "off") == 0)
6683 syn_time_on = FALSE;
6684 else if (STRCMP(eap->arg, "clear") == 0)
6685 syntime_clear();
6686 else if (STRCMP(eap->arg, "report") == 0)
6687 syntime_report();
6688 else
6689 EMSG2(_(e_invarg2), eap->arg);
6690}
6691
6692 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006693syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006694{
6695 profile_zero(&st->total);
6696 profile_zero(&st->slowest);
6697 st->count = 0;
6698 st->match = 0;
6699}
6700
6701/*
6702 * Clear the syntax timing for the current buffer.
6703 */
6704 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006705syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006706{
6707 int idx;
6708 synpat_T *spp;
6709
6710 if (!syntax_present(curwin))
6711 {
6712 MSG(_(msg_no_items));
6713 return;
6714 }
6715 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6716 {
6717 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6718 syn_clear_time(&spp->sp_time);
6719 }
6720}
6721
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006722#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6723/*
6724 * Function given to ExpandGeneric() to obtain the possible arguments of the
6725 * ":syntime {on,off,clear,report}" command.
6726 */
6727 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006728get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006729{
6730 switch (idx)
6731 {
6732 case 0: return (char_u *)"on";
6733 case 1: return (char_u *)"off";
6734 case 2: return (char_u *)"clear";
6735 case 3: return (char_u *)"report";
6736 }
6737 return NULL;
6738}
6739#endif
6740
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006741typedef struct
6742{
6743 proftime_T total;
6744 int count;
6745 int match;
6746 proftime_T slowest;
6747 proftime_T average;
6748 int id;
6749 char_u *pattern;
6750} time_entry_T;
6751
6752 static int
6753#ifdef __BORLANDC__
6754_RTLENTRYF
6755#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006756syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006757{
6758 const time_entry_T *s1 = v1;
6759 const time_entry_T *s2 = v2;
6760
6761 return profile_cmp(&s1->total, &s2->total);
6762}
6763
6764/*
6765 * Clear the syntax timing for the current buffer.
6766 */
6767 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006768syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006769{
6770 int idx;
6771 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006772# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006773 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006774# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006775 int len;
6776 proftime_T total_total;
6777 int total_count = 0;
6778 garray_T ga;
6779 time_entry_T *p;
6780
6781 if (!syntax_present(curwin))
6782 {
6783 MSG(_(msg_no_items));
6784 return;
6785 }
6786
6787 ga_init2(&ga, sizeof(time_entry_T), 50);
6788 profile_zero(&total_total);
6789 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6790 {
6791 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6792 if (spp->sp_time.count > 0)
6793 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006794 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006795 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6796 p->total = spp->sp_time.total;
6797 profile_add(&total_total, &spp->sp_time.total);
6798 p->count = spp->sp_time.count;
6799 p->match = spp->sp_time.match;
6800 total_count += spp->sp_time.count;
6801 p->slowest = spp->sp_time.slowest;
6802# ifdef FEAT_FLOAT
6803 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6804 p->average = tm;
6805# endif
6806 p->id = spp->sp_syn.id;
6807 p->pattern = spp->sp_pattern;
6808 ++ga.ga_len;
6809 }
6810 }
6811
Bram Moolenaara2162552017-01-08 17:46:20 +01006812 /* Sort on total time. Skip if there are no items to avoid passing NULL
6813 * pointer to qsort(). */
6814 if (ga.ga_len > 1)
6815 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006816 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006817
6818 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6819 MSG_PUTS("\n");
6820 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6821 {
6822 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6823 p = ((time_entry_T *)ga.ga_data) + idx;
6824
6825 MSG_PUTS(profile_msg(&p->total));
6826 MSG_PUTS(" "); /* make sure there is always a separating space */
6827 msg_advance(13);
6828 msg_outnum(p->count);
6829 MSG_PUTS(" ");
6830 msg_advance(20);
6831 msg_outnum(p->match);
6832 MSG_PUTS(" ");
6833 msg_advance(26);
6834 MSG_PUTS(profile_msg(&p->slowest));
6835 MSG_PUTS(" ");
6836 msg_advance(38);
6837# ifdef FEAT_FLOAT
6838 MSG_PUTS(profile_msg(&p->average));
6839 MSG_PUTS(" ");
6840# endif
6841 msg_advance(50);
6842 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6843 MSG_PUTS(" ");
6844
6845 msg_advance(69);
6846 if (Columns < 80)
6847 len = 20; /* will wrap anyway */
6848 else
6849 len = Columns - 70;
6850 if (len > (int)STRLEN(p->pattern))
6851 len = (int)STRLEN(p->pattern);
6852 msg_outtrans_len(p->pattern, len);
6853 MSG_PUTS("\n");
6854 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006855 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006856 if (!got_int)
6857 {
6858 MSG_PUTS("\n");
6859 MSG_PUTS(profile_msg(&total_total));
6860 msg_advance(13);
6861 msg_outnum(total_count);
6862 MSG_PUTS("\n");
6863 }
6864}
6865#endif
6866
Bram Moolenaar071d4272004-06-13 20:20:40 +00006867#endif /* FEAT_SYN_HL */
6868
Bram Moolenaar071d4272004-06-13 20:20:40 +00006869/**************************************
6870 * Highlighting stuff *
6871 **************************************/
6872
6873/*
6874 * The default highlight groups. These are compiled-in for fast startup and
6875 * they still work when the runtime files can't be found.
6876 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006877 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6878 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006879 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006880#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006881# define CENT(a, b) b
6882#else
6883# define CENT(a, b) a
6884#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006885static char *(highlight_init_both[]) = {
6886 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6887 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6888 CENT("IncSearch term=reverse cterm=reverse",
6889 "IncSearch term=reverse cterm=reverse gui=reverse"),
6890 CENT("ModeMsg term=bold cterm=bold",
6891 "ModeMsg term=bold cterm=bold gui=bold"),
6892 CENT("NonText term=bold ctermfg=Blue",
6893 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6894 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6895 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6896 CENT("StatusLineNC term=reverse cterm=reverse",
6897 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
6898 "default link EndOfBuffer NonText",
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006899 CENT("VertSplit term=reverse cterm=reverse",
6900 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006901#ifdef FEAT_CLIPBOARD
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006902 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6903 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006904#endif
6905#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006906 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6907 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006908#endif
6909#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006910 CENT("PmenuSbar ctermbg=Grey",
6911 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006912#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006913 CENT("TabLineSel term=bold cterm=bold",
6914 "TabLineSel term=bold cterm=bold gui=bold"),
6915 CENT("TabLineFill term=reverse cterm=reverse",
6916 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00006917#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006918 "Cursor guibg=fg guifg=bg",
6919 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006920#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006921 "default link QuickFixLine Search",
6922 NULL
6923};
Bram Moolenaar071d4272004-06-13 20:20:40 +00006924
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006925/* Default colors only used with a light background. */
6926static char *(highlight_init_light[]) = {
6927 CENT("Directory term=bold ctermfg=DarkBlue",
6928 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6929 CENT("LineNr term=underline ctermfg=Brown",
6930 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6931 CENT("CursorLineNr term=bold ctermfg=Brown",
6932 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
6933 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6934 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6935 CENT("Question term=standout ctermfg=DarkGreen",
6936 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6937 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6938 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006939#ifdef FEAT_SPELL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006940 CENT("SpellBad term=reverse ctermbg=LightRed",
6941 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6942 CENT("SpellCap term=reverse ctermbg=LightBlue",
6943 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6944 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6945 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6946 CENT("SpellLocal term=underline ctermbg=Cyan",
6947 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006948#endif
6949#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006950 CENT("PmenuThumb ctermbg=Black",
6951 "PmenuThumb ctermbg=Black guibg=Black"),
6952 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6953 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6954 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6955 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006956#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006957 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6958 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6959 CENT("Title term=bold ctermfg=DarkMagenta",
6960 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6961 CENT("WarningMsg term=standout ctermfg=DarkRed",
6962 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006963#ifdef FEAT_WILDMENU
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006964 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6965 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006966#endif
6967#ifdef FEAT_FOLDING
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006968 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6969 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6970 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6971 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006972#endif
6973#ifdef FEAT_SIGNS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006974 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6975 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006976#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006977 CENT("Visual term=reverse",
6978 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006979#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006980 CENT("DiffAdd term=bold ctermbg=LightBlue",
6981 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6982 CENT("DiffChange term=bold ctermbg=LightMagenta",
6983 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6984 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6985 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006986#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006987 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6988 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006989#ifdef FEAT_SYN_HL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006990 CENT("CursorColumn term=reverse ctermbg=LightGrey",
6991 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
6992 CENT("CursorLine term=underline cterm=underline",
6993 "CursorLine term=underline cterm=underline guibg=Grey90"),
6994 CENT("ColorColumn term=reverse ctermbg=LightRed",
6995 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006996#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006997#ifdef FEAT_CONCEAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006998 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6999 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02007000#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007001#ifdef FEAT_AUTOCMD
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007002 CENT("MatchParen term=reverse ctermbg=Cyan",
7003 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007004#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007005#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007006 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007007#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007008#ifdef FEAT_TERMINAL
7009 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
7010 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
7011 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
7012 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
7013#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007014#ifdef FEAT_MENU
7015 CENT("ToolbarLine term=underline ctermbg=LightGrey",
7016 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
7017 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02007018 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007019#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007020 NULL
7021};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007022
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007023/* Default colors only used with a dark background. */
7024static char *(highlight_init_dark[]) = {
7025 CENT("Directory term=bold ctermfg=LightCyan",
7026 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
7027 CENT("LineNr term=underline ctermfg=Yellow",
7028 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
7029 CENT("CursorLineNr term=bold ctermfg=Yellow",
7030 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
7031 CENT("MoreMsg term=bold ctermfg=LightGreen",
7032 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
7033 CENT("Question term=standout ctermfg=LightGreen",
7034 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
7035 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
7036 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
7037 CENT("SpecialKey term=bold ctermfg=LightBlue",
7038 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007039#ifdef FEAT_SPELL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007040 CENT("SpellBad term=reverse ctermbg=Red",
7041 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
7042 CENT("SpellCap term=reverse ctermbg=Blue",
7043 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
7044 CENT("SpellRare term=reverse ctermbg=Magenta",
7045 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
7046 CENT("SpellLocal term=underline ctermbg=Cyan",
7047 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007048#endif
7049#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007050 CENT("PmenuThumb ctermbg=White",
7051 "PmenuThumb ctermbg=White guibg=White"),
7052 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
7053 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
7054 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
7055 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007056#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007057 CENT("Title term=bold ctermfg=LightMagenta",
7058 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
7059 CENT("WarningMsg term=standout ctermfg=LightRed",
7060 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007061#ifdef FEAT_WILDMENU
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007062 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
7063 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007064#endif
7065#ifdef FEAT_FOLDING
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007066 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
7067 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
7068 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7069 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007070#endif
7071#ifdef FEAT_SIGNS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007072 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7073 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007074#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007075 CENT("Visual term=reverse",
7076 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007077#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007078 CENT("DiffAdd term=bold ctermbg=DarkBlue",
7079 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
7080 CENT("DiffChange term=bold ctermbg=DarkMagenta",
7081 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
7082 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
7083 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007084#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007085 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7086 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007087#ifdef FEAT_SYN_HL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007088 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
7089 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
7090 CENT("CursorLine term=underline cterm=underline",
7091 "CursorLine term=underline cterm=underline guibg=Grey40"),
7092 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7093 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007094#endif
7095#ifdef FEAT_AUTOCMD
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007096 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7097 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007098#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02007099#ifdef FEAT_CONCEAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007100 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7101 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02007102#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007103#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007104 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007105#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007106#ifdef FEAT_TERMINAL
7107 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
7108 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
7109 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
7110 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
7111#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007112#ifdef FEAT_MENU
7113 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02007114 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007115 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
7116 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
7117#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007118 NULL
7119};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007120
7121 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007122init_highlight(
7123 int both, /* include groups where 'bg' doesn't matter */
7124 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007125{
7126 int i;
7127 char **pp;
7128 static int had_both = FALSE;
7129#ifdef FEAT_EVAL
7130 char_u *p;
7131
7132 /*
7133 * Try finding the color scheme file. Used when a color file was loaded
7134 * and 'background' or 't_Co' is changed.
7135 */
7136 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007137 if (p != NULL)
7138 {
7139 /* The value of g:colors_name could be freed when sourcing the script,
7140 * making "p" invalid, so copy it. */
7141 char_u *copy_p = vim_strsave(p);
7142 int r;
7143
7144 if (copy_p != NULL)
7145 {
7146 r = load_colors(copy_p);
7147 vim_free(copy_p);
7148 if (r == OK)
7149 return;
7150 }
7151 }
7152
Bram Moolenaar071d4272004-06-13 20:20:40 +00007153#endif
7154
7155 /*
7156 * Didn't use a color file, use the compiled-in colors.
7157 */
7158 if (both)
7159 {
7160 had_both = TRUE;
7161 pp = highlight_init_both;
7162 for (i = 0; pp[i] != NULL; ++i)
7163 do_highlight((char_u *)pp[i], reset, TRUE);
7164 }
7165 else if (!had_both)
7166 /* Don't do anything before the call with both == TRUE from main().
7167 * Not everything has been setup then, and that call will overrule
7168 * everything anyway. */
7169 return;
7170
7171 if (*p_bg == 'l')
7172 pp = highlight_init_light;
7173 else
7174 pp = highlight_init_dark;
7175 for (i = 0; pp[i] != NULL; ++i)
7176 do_highlight((char_u *)pp[i], reset, TRUE);
7177
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007178 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007179 * depend on the number of colors available.
7180 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007181 * to avoid Statement highlighted text disappears.
7182 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007183 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007184 do_highlight((char_u *)(*p_bg == 'l'
7185 ? "Visual cterm=NONE ctermbg=LightGrey"
7186 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007187 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007188 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007189 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7190 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007191 if (*p_bg == 'l')
7192 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7193 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007194
Bram Moolenaar071d4272004-06-13 20:20:40 +00007195#ifdef FEAT_SYN_HL
7196 /*
7197 * If syntax highlighting is enabled load the highlighting for it.
7198 */
7199 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007200 {
7201 static int recursive = 0;
7202
7203 if (recursive >= 5)
7204 EMSG(_("E679: recursive loop loading syncolor.vim"));
7205 else
7206 {
7207 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007208 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007209 --recursive;
7210 }
7211 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007212#endif
7213}
7214
7215/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007216 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007217 * Return OK for success, FAIL for failure.
7218 */
7219 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007220load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007221{
7222 char_u *buf;
7223 int retval = FAIL;
7224 static int recursive = FALSE;
7225
7226 /* When being called recursively, this is probably because setting
7227 * 'background' caused the highlighting to be reloaded. This means it is
7228 * working, thus we should return OK. */
7229 if (recursive)
7230 return OK;
7231
7232 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007233 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007234 if (buf != NULL)
7235 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007236 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007237 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007238 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007239#ifdef FEAT_AUTOCMD
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007240 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007241#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007242 }
7243 recursive = FALSE;
7244
7245 return retval;
7246}
7247
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007248static char *(color_names[28]) = {
7249 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7250 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7251 "Gray", "Grey", "LightGray", "LightGrey",
7252 "DarkGray", "DarkGrey",
7253 "Blue", "LightBlue", "Green", "LightGreen",
7254 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7255 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7256 /* indices:
7257 * 0, 1, 2, 3,
7258 * 4, 5, 6, 7,
7259 * 8, 9, 10, 11,
7260 * 12, 13,
7261 * 14, 15, 16, 17,
7262 * 18, 19, 20, 21, 22,
7263 * 23, 24, 25, 26, 27 */
7264static int color_numbers_16[28] = {0, 1, 2, 3,
7265 4, 5, 6, 6,
7266 7, 7, 7, 7,
7267 8, 8,
7268 9, 9, 10, 10,
7269 11, 11, 12, 12, 13,
7270 13, 14, 14, 15, -1};
7271/* for xterm with 88 colors... */
7272static int color_numbers_88[28] = {0, 4, 2, 6,
7273 1, 5, 32, 72,
7274 84, 84, 7, 7,
7275 82, 82,
7276 12, 43, 10, 61,
7277 14, 63, 9, 74, 13,
7278 75, 11, 78, 15, -1};
7279/* for xterm with 256 colors... */
7280static int color_numbers_256[28] = {0, 4, 2, 6,
7281 1, 5, 130, 130,
7282 248, 248, 7, 7,
7283 242, 242,
7284 12, 81, 10, 121,
7285 14, 159, 9, 224, 13,
7286 225, 11, 229, 15, -1};
7287/* for terminals with less than 16 colors... */
7288static int color_numbers_8[28] = {0, 4, 2, 6,
7289 1, 5, 3, 3,
7290 7, 7, 7, 7,
7291 0+8, 0+8,
7292 4+8, 4+8, 2+8, 2+8,
7293 6+8, 6+8, 1+8, 1+8, 5+8,
7294 5+8, 3+8, 3+8, 7+8, -1};
7295
7296/*
7297 * Lookup the "cterm" value to be used for color with index "idx" in
7298 * color_names[].
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007299 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
7300 * colors, otherwise it will be unchanged.
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007301 */
7302 int
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007303lookup_color(int idx, int foreground, int *boldp)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007304{
7305 int color = color_numbers_16[idx];
7306 char_u *p;
7307
7308 /* Use the _16 table to check if it's a valid color name. */
7309 if (color < 0)
7310 return -1;
7311
7312 if (t_colors == 8)
7313 {
7314 /* t_Co is 8: use the 8 colors table */
7315#if defined(__QNXNTO__)
7316 color = color_numbers_8_qansi[idx];
7317#else
7318 color = color_numbers_8[idx];
7319#endif
7320 if (foreground)
7321 {
7322 /* set/reset bold attribute to get light foreground
7323 * colors (on some terminals, e.g. "linux") */
7324 if (color & 8)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007325 *boldp = TRUE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007326 else
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007327 *boldp = FALSE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007328 }
7329 color &= 7; /* truncate to 8 colors */
7330 }
7331 else if (t_colors == 16 || t_colors == 88
7332 || t_colors >= 256)
7333 {
7334 /*
7335 * Guess: if the termcap entry ends in 'm', it is
7336 * probably an xterm-like terminal. Use the changed
7337 * order for colors.
7338 */
7339 if (*T_CAF != NUL)
7340 p = T_CAF;
7341 else
7342 p = T_CSF;
7343 if (*p != NUL && (t_colors > 256
7344 || *(p + STRLEN(p) - 1) == 'm'))
7345 {
7346 if (t_colors == 88)
7347 color = color_numbers_88[idx];
7348 else if (t_colors >= 256)
7349 color = color_numbers_256[idx];
7350 else
7351 color = color_numbers_8[idx];
7352 }
7353 }
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 Moolenaar071d4272004-06-13 20:20:40 +00007381 int dodefault = FALSE;
7382 int doclear = FALSE;
7383 int dolink = FALSE;
7384 int error = FALSE;
7385 int color;
7386 int is_normal_group = FALSE; /* "Normal" group */
7387#ifdef FEAT_GUI_X11
7388 int is_menu_group = FALSE; /* "Menu" group */
7389 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7390 int is_tooltip_group = FALSE; /* "Tooltip" group */
7391 int do_colors = FALSE; /* need to update colors? */
7392#else
7393# define is_menu_group 0
7394# define is_tooltip_group 0
7395#endif
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007396#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
7397 int did_highlight_changed = FALSE;
7398#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007399
7400 /*
7401 * If no argument, list current highlighting.
7402 */
7403 if (ends_excmd(*line))
7404 {
7405 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7406 /* TODO: only call when the group has attributes set */
7407 highlight_list_one((int)i);
7408 return;
7409 }
7410
7411 /*
7412 * Isolate the name.
7413 */
7414 name_end = skiptowhite(line);
7415 linep = skipwhite(name_end);
7416
7417 /*
7418 * Check for "default" argument.
7419 */
7420 if (STRNCMP(line, "default", name_end - line) == 0)
7421 {
7422 dodefault = TRUE;
7423 line = linep;
7424 name_end = skiptowhite(line);
7425 linep = skipwhite(name_end);
7426 }
7427
7428 /*
7429 * Check for "clear" or "link" argument.
7430 */
7431 if (STRNCMP(line, "clear", name_end - line) == 0)
7432 doclear = TRUE;
7433 if (STRNCMP(line, "link", name_end - line) == 0)
7434 dolink = TRUE;
7435
7436 /*
7437 * ":highlight {group-name}": list highlighting for one group.
7438 */
7439 if (!doclear && !dolink && ends_excmd(*linep))
7440 {
7441 id = syn_namen2id(line, (int)(name_end - line));
7442 if (id == 0)
7443 EMSG2(_("E411: highlight group not found: %s"), line);
7444 else
7445 highlight_list_one(id);
7446 return;
7447 }
7448
7449 /*
7450 * Handle ":highlight link {from} {to}" command.
7451 */
7452 if (dolink)
7453 {
7454 char_u *from_start = linep;
7455 char_u *from_end;
7456 char_u *to_start;
7457 char_u *to_end;
7458 int from_id;
7459 int to_id;
7460
7461 from_end = skiptowhite(from_start);
7462 to_start = skipwhite(from_end);
7463 to_end = skiptowhite(to_start);
7464
7465 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7466 {
7467 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7468 from_start);
7469 return;
7470 }
7471
7472 if (!ends_excmd(*skipwhite(to_end)))
7473 {
7474 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7475 return;
7476 }
7477
7478 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7479 if (STRNCMP(to_start, "NONE", 4) == 0)
7480 to_id = 0;
7481 else
7482 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7483
Bram Moolenaar414168d2017-09-10 15:21:55 +02007484 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007485 {
7486 /*
7487 * Don't allow a link when there already is some highlighting
7488 * for the group, unless '!' is used
7489 */
7490 if (to_id > 0 && !forceit && !init
7491 && hl_has_settings(from_id - 1, dodefault))
7492 {
7493 if (sourcing_name == NULL && !dodefault)
7494 EMSG(_("E414: group has settings, highlight link ignored"));
7495 }
Bram Moolenaar414168d2017-09-10 15:21:55 +02007496 else if (HL_TABLE()[from_id - 1].sg_link != to_id
Bram Moolenaar99433292017-09-08 12:37:47 +02007497#ifdef FEAT_EVAL
Bram Moolenaar414168d2017-09-10 15:21:55 +02007498 || HL_TABLE()[from_id - 1].sg_scriptID != current_SID
Bram Moolenaar99433292017-09-08 12:37:47 +02007499#endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007500 || HL_TABLE()[from_id - 1].sg_cleared)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007501 {
7502 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007503 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7504 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007505#ifdef FEAT_EVAL
Bram Moolenaar414168d2017-09-10 15:21:55 +02007506 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007507#endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007508 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007509 redraw_all_later(SOME_VALID);
Bram Moolenaar99433292017-09-08 12:37:47 +02007510
7511 /* Only call highlight_changed() once after multiple changes. */
7512 need_highlight_changed = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007513 }
7514 }
7515
Bram Moolenaar071d4272004-06-13 20:20:40 +00007516 return;
7517 }
7518
7519 if (doclear)
7520 {
7521 /*
7522 * ":highlight clear [group]" command.
7523 */
7524 line = linep;
7525 if (ends_excmd(*line))
7526 {
7527#ifdef FEAT_GUI
7528 /* First, we do not destroy the old values, but allocate the new
7529 * ones and update the display. THEN we destroy the old values.
7530 * If we destroy the old values first, then the old values
7531 * (such as GuiFont's or GuiFontset's) will still be displayed but
7532 * invalid because they were free'd.
7533 */
7534 if (gui.in_use)
7535 {
7536# ifdef FEAT_BEVAL_TIP
7537 gui_init_tooltip_font();
7538# endif
7539# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7540 gui_init_menu_font();
7541# endif
7542 }
7543# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7544 gui_mch_def_colors();
7545# endif
7546# ifdef FEAT_GUI_X11
7547# ifdef FEAT_MENU
7548
7549 /* This only needs to be done when there is no Menu highlight
7550 * group defined by default, which IS currently the case.
7551 */
7552 gui_mch_new_menu_colors();
7553# endif
7554 if (gui.in_use)
7555 {
7556 gui_new_scrollbar_colors();
7557# ifdef FEAT_BEVAL
7558 gui_mch_new_tooltip_colors();
7559# endif
7560# ifdef FEAT_MENU
7561 gui_mch_new_menu_font();
7562# endif
7563 }
7564# endif
7565
7566 /* Ok, we're done allocating the new default graphics items.
7567 * The screen should already be refreshed at this point.
7568 * It is now Ok to clear out the old data.
7569 */
7570#endif
7571#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007572 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007573#endif
7574 restore_cterm_colors();
7575
7576 /*
7577 * Clear all default highlight groups and load the defaults.
7578 */
7579 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7580 highlight_clear(idx);
7581 init_highlight(TRUE, TRUE);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007582#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007583 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007584 highlight_gui_started();
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007585 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007586#endif
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007587 highlight_changed();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007588 redraw_later_clear();
7589 return;
7590 }
7591 name_end = skiptowhite(line);
7592 linep = skipwhite(name_end);
7593 }
7594
7595 /*
7596 * Find the group name in the table. If it does not exist yet, add it.
7597 */
7598 id = syn_check_group(line, (int)(name_end - line));
7599 if (id == 0) /* failed (out of memory) */
7600 return;
7601 idx = id - 1; /* index is ID minus one */
7602
7603 /* Return if "default" was used and the group already has settings. */
7604 if (dodefault && hl_has_settings(idx, TRUE))
7605 return;
7606
Bram Moolenaar99433292017-09-08 12:37:47 +02007607 /* Make a copy so we can check if any attribute actually changed. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007608 item_before = HL_TABLE()[idx];
Bram Moolenaar99433292017-09-08 12:37:47 +02007609
Bram Moolenaar414168d2017-09-10 15:21:55 +02007610 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007611 is_normal_group = TRUE;
7612#ifdef FEAT_GUI_X11
Bram Moolenaar414168d2017-09-10 15:21:55 +02007613 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007614 is_menu_group = TRUE;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007615 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007616 is_scrollbar_group = TRUE;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007617 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007618 is_tooltip_group = TRUE;
7619#endif
7620
7621 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7622 if (doclear || (forceit && init))
7623 {
7624 highlight_clear(idx);
7625 if (!doclear)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007626 HL_TABLE()[idx].sg_set = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007627 }
7628
7629 if (!doclear)
7630 while (!ends_excmd(*linep))
7631 {
7632 key_start = linep;
7633 if (*linep == '=')
7634 {
7635 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7636 error = TRUE;
7637 break;
7638 }
7639
7640 /*
7641 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7642 * "guibg").
7643 */
Bram Moolenaar1c465442017-03-12 20:10:05 +01007644 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00007645 ++linep;
7646 vim_free(key);
7647 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7648 if (key == NULL)
7649 {
7650 error = TRUE;
7651 break;
7652 }
7653 linep = skipwhite(linep);
7654
7655 if (STRCMP(key, "NONE") == 0)
7656 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007657 if (!init || HL_TABLE()[idx].sg_set == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007658 {
7659 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007660 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007661 highlight_clear(idx);
7662 }
7663 continue;
7664 }
7665
7666 /*
7667 * Check for the equal sign.
7668 */
7669 if (*linep != '=')
7670 {
7671 EMSG2(_("E416: missing equal sign: %s"), key_start);
7672 error = TRUE;
7673 break;
7674 }
7675 ++linep;
7676
7677 /*
7678 * Isolate the argument.
7679 */
7680 linep = skipwhite(linep);
7681 if (*linep == '\'') /* guifg='color name' */
7682 {
7683 arg_start = ++linep;
7684 linep = vim_strchr(linep, '\'');
7685 if (linep == NULL)
7686 {
7687 EMSG2(_(e_invarg2), key_start);
7688 error = TRUE;
7689 break;
7690 }
7691 }
7692 else
7693 {
7694 arg_start = linep;
7695 linep = skiptowhite(linep);
7696 }
7697 if (linep == arg_start)
7698 {
7699 EMSG2(_("E417: missing argument: %s"), key_start);
7700 error = TRUE;
7701 break;
7702 }
7703 vim_free(arg);
7704 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7705 if (arg == NULL)
7706 {
7707 error = TRUE;
7708 break;
7709 }
7710 if (*linep == '\'')
7711 ++linep;
7712
7713 /*
7714 * Store the argument.
7715 */
7716 if ( STRCMP(key, "TERM") == 0
7717 || STRCMP(key, "CTERM") == 0
7718 || STRCMP(key, "GUI") == 0)
7719 {
7720 attr = 0;
7721 off = 0;
7722 while (arg[off] != NUL)
7723 {
7724 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7725 {
7726 len = (int)STRLEN(hl_name_table[i]);
7727 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7728 {
7729 attr |= hl_attr_table[i];
7730 off += len;
7731 break;
7732 }
7733 }
7734 if (i < 0)
7735 {
7736 EMSG2(_("E418: Illegal value: %s"), arg);
7737 error = TRUE;
7738 break;
7739 }
7740 if (arg[off] == ',') /* another one follows */
7741 ++off;
7742 }
7743 if (error)
7744 break;
7745 if (*key == 'T')
7746 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007747 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007748 {
7749 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007750 HL_TABLE()[idx].sg_set |= SG_TERM;
7751 HL_TABLE()[idx].sg_term = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007752 }
7753 }
7754 else if (*key == 'C')
7755 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007756 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007757 {
7758 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007759 HL_TABLE()[idx].sg_set |= SG_CTERM;
7760 HL_TABLE()[idx].sg_cterm = attr;
7761 HL_TABLE()[idx].sg_cterm_bold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007762 }
7763 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007764#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007765 else
7766 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007767 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007768 {
7769 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007770 HL_TABLE()[idx].sg_set |= SG_GUI;
7771 HL_TABLE()[idx].sg_gui = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007772 }
7773 }
7774#endif
7775 }
7776 else if (STRCMP(key, "FONT") == 0)
7777 {
7778 /* in non-GUI fonts are simply ignored */
7779#ifdef FEAT_GUI
Bram Moolenaar414168d2017-09-10 15:21:55 +02007780 if (HL_TABLE()[idx].sg_font_name != NULL
7781 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
Bram Moolenaar99433292017-09-08 12:37:47 +02007782 {
7783 /* Font name didn't change, ignore. */
7784 }
7785 else if (!gui.shell_created)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007786 {
7787 /* GUI not started yet, always accept the name. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007788 vim_free(HL_TABLE()[idx].sg_font_name);
7789 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007790 }
7791 else
7792 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007793 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007794# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007795 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007796# endif
7797 /* First, save the current font/fontset.
7798 * Then try to allocate the font/fontset.
Bram Moolenaar414168d2017-09-10 15:21:55 +02007799 * If the allocation fails, HL_TABLE()[idx].sg_font OR
Bram Moolenaar071d4272004-06-13 20:20:40 +00007800 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7801 */
7802
Bram Moolenaar414168d2017-09-10 15:21:55 +02007803 HL_TABLE()[idx].sg_font = NOFONT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007804# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007805 HL_TABLE()[idx].sg_fontset = NOFONTSET;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007806# endif
7807 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007808 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007809
7810# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007811 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007812 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007813 /* New fontset was accepted. Free the old one, if there
7814 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007815 gui_mch_free_fontset(temp_sg_fontset);
Bram Moolenaar414168d2017-09-10 15:21:55 +02007816 vim_free(HL_TABLE()[idx].sg_font_name);
7817 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007818 }
7819 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007820 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007821# endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007822 if (HL_TABLE()[idx].sg_font != NOFONT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007823 {
7824 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007825 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007826 gui_mch_free_font(temp_sg_font);
Bram Moolenaar414168d2017-09-10 15:21:55 +02007827 vim_free(HL_TABLE()[idx].sg_font_name);
7828 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007829 }
7830 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007831 HL_TABLE()[idx].sg_font = temp_sg_font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007832 }
7833#endif
7834 }
7835 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7836 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007837 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007838 {
7839 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007840 HL_TABLE()[idx].sg_set |= SG_CTERM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007841
7842 /* When setting the foreground color, and previously the "bold"
7843 * flag was set for a light color, reset it now */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007844 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007845 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007846 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7847 HL_TABLE()[idx].sg_cterm_bold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007848 }
7849
7850 if (VIM_ISDIGIT(*arg))
7851 color = atoi((char *)arg);
7852 else if (STRICMP(arg, "fg") == 0)
7853 {
7854 if (cterm_normal_fg_color)
7855 color = cterm_normal_fg_color - 1;
7856 else
7857 {
7858 EMSG(_("E419: FG color unknown"));
7859 error = TRUE;
7860 break;
7861 }
7862 }
7863 else if (STRICMP(arg, "bg") == 0)
7864 {
7865 if (cterm_normal_bg_color > 0)
7866 color = cterm_normal_bg_color - 1;
7867 else
7868 {
7869 EMSG(_("E420: BG color unknown"));
7870 error = TRUE;
7871 break;
7872 }
7873 }
7874 else
7875 {
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007876 int bold = MAYBE;
7877
Bram Moolenaar071d4272004-06-13 20:20:40 +00007878#if defined(__QNXNTO__)
7879 static int *color_numbers_8_qansi = color_numbers_8;
7880 /* On qnx, the 8 & 16 color arrays are the same */
7881 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7882 color_numbers_8_qansi = color_numbers_16;
7883#endif
7884
7885 /* reduce calls to STRICMP a bit, it can be slow */
7886 off = TOUPPER_ASC(*arg);
7887 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7888 if (off == color_names[i][0]
7889 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7890 break;
7891 if (i < 0)
7892 {
7893 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7894 error = TRUE;
7895 break;
7896 }
7897
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007898 color = lookup_color(i, key[5] == 'F', &bold);
7899
7900 /* set/reset bold attribute to get light foreground
7901 * colors (on some terminals, e.g. "linux") */
7902 if (bold == TRUE)
7903 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007904 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7905 HL_TABLE()[idx].sg_cterm_bold = TRUE;
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007906 }
7907 else if (bold == FALSE)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007908 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007909 }
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007910
Bram Moolenaarccbab932010-05-13 15:40:30 +02007911 /* Add one to the argument, to avoid zero. Zero is used for
7912 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007913 if (key[5] == 'F')
7914 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007915 HL_TABLE()[idx].sg_cterm_fg = color + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007916 if (is_normal_group)
7917 {
7918 cterm_normal_fg_color = color + 1;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007919 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007920#ifdef FEAT_GUI
7921 /* Don't do this if the GUI is used. */
7922 if (!gui.in_use && !gui.starting)
7923#endif
7924 {
7925 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007926 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007927 term_fg_color(color);
7928 }
7929 }
7930 }
7931 else
7932 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007933 HL_TABLE()[idx].sg_cterm_bg = color + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007934 if (is_normal_group)
7935 {
7936 cterm_normal_bg_color = color + 1;
7937#ifdef FEAT_GUI
7938 /* Don't mess with 'background' if the GUI is used. */
7939 if (!gui.in_use && !gui.starting)
7940#endif
7941 {
7942 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007943 if (color >= 0)
7944 {
Bram Moolenaar1615b362017-06-04 21:06:09 +02007945 int dark = -1;
7946
Bram Moolenaarccbab932010-05-13 15:40:30 +02007947 if (termcap_active)
7948 term_bg_color(color);
7949 if (t_colors < 16)
Bram Moolenaar1615b362017-06-04 21:06:09 +02007950 dark = (color == 0 || color == 4);
7951 /* Limit the heuristic to the standard 16 colors */
7952 else if (color < 16)
7953 dark = (color < 7 || color == 8);
Bram Moolenaarccbab932010-05-13 15:40:30 +02007954 /* Set the 'background' option if the value is
7955 * wrong. */
Bram Moolenaar1615b362017-06-04 21:06:09 +02007956 if (dark != -1
7957 && dark != (*p_bg == 'd')
7958 && !option_was_set((char_u *)"bg"))
7959 {
Bram Moolenaarccbab932010-05-13 15:40:30 +02007960 set_option_value((char_u *)"bg", 0L,
Bram Moolenaar1615b362017-06-04 21:06:09 +02007961 (char_u *)(dark ? "dark" : "light"), 0);
7962 reset_option_was_set((char_u *)"bg");
7963 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007964 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007965 }
7966 }
7967 }
7968 }
7969 }
7970 else if (STRCMP(key, "GUIFG") == 0)
7971 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02007972#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02007973 char_u **namep = &HL_TABLE()[idx].sg_gui_fg_name;
7974
Bram Moolenaar414168d2017-09-10 15:21:55 +02007975 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007976 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007977 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007978 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007979
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007980# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007981 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007982 i = color_name2handle(arg);
Bram Moolenaar63122562016-04-30 12:28:15 +02007983 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007984 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007985 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007986# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02007987 if (*namep == NULL || STRCMP(*namep, arg) != 0)
7988 {
7989 vim_free(*namep);
7990 if (STRCMP(arg, "NONE") != 0)
7991 *namep = vim_strsave(arg);
7992 else
7993 *namep = NULL;
7994 }
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007995# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007996# ifdef FEAT_GUI_X11
Bram Moolenaar452030e2017-09-25 22:57:27 +02007997 if (is_menu_group && gui.menu_fg_pixel != i)
7998 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007999 gui.menu_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008000 do_colors = TRUE;
8001 }
8002 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
8003 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008004 gui.scroll_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008005 do_colors = TRUE;
8006 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008007# ifdef FEAT_BEVAL
Bram Moolenaar452030e2017-09-25 22:57:27 +02008008 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
8009 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008010 gui.tooltip_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008011 do_colors = TRUE;
8012 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008013# endif
Bram Moolenaar61623362010-07-14 22:04:22 +02008014# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008015 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008016# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008017 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008018#endif
8019 }
8020 else if (STRCMP(key, "GUIBG") == 0)
8021 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02008022#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02008023 char_u **namep = &HL_TABLE()[idx].sg_gui_bg_name;
8024
Bram Moolenaar414168d2017-09-10 15:21:55 +02008025 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008026 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008027 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008028 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008029
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008030# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008031 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008032 i = color_name2handle(arg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008033 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008034 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008035 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008036# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02008037 if (*namep == NULL || STRCMP(*namep, arg) != 0)
8038 {
8039 vim_free(*namep);
8040 if (STRCMP(arg, "NONE") != 0)
8041 *namep = vim_strsave(arg);
8042 else
8043 *namep = NULL;
8044 }
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008045# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008046# ifdef FEAT_GUI_X11
Bram Moolenaar452030e2017-09-25 22:57:27 +02008047 if (is_menu_group && gui.menu_bg_pixel != i)
8048 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008049 gui.menu_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008050 do_colors = TRUE;
8051 }
8052 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
8053 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008054 gui.scroll_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008055 do_colors = TRUE;
8056 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008057# ifdef FEAT_BEVAL
Bram Moolenaar452030e2017-09-25 22:57:27 +02008058 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
8059 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008060 gui.tooltip_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008061 do_colors = TRUE;
8062 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008063# endif
Bram Moolenaar61623362010-07-14 22:04:22 +02008064# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008065 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008066# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008067 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008068#endif
8069 }
8070 else if (STRCMP(key, "GUISP") == 0)
8071 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02008072#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02008073 char_u **namep = &HL_TABLE()[idx].sg_gui_sp_name;
8074
Bram Moolenaar414168d2017-09-10 15:21:55 +02008075 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008076 {
8077 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008078 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008079
Bram Moolenaar61623362010-07-14 22:04:22 +02008080# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008081 i = color_name2handle(arg);
8082 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
8083 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008084 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008085# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02008086 if (*namep == NULL || STRCMP(*namep, arg) != 0)
8087 {
8088 vim_free(*namep);
8089 if (STRCMP(arg, "NONE") != 0)
8090 *namep = vim_strsave(arg);
8091 else
8092 *namep = NULL;
8093 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008094# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008095 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008096# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008097 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008098#endif
8099 }
8100 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
8101 {
8102 char_u buf[100];
8103 char_u *tname;
8104
8105 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008106 HL_TABLE()[idx].sg_set |= SG_TERM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008107
8108 /*
8109 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008110 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008111 */
8112 if (STRNCMP(arg, "t_", 2) == 0)
8113 {
8114 off = 0;
8115 buf[0] = 0;
8116 while (arg[off] != NUL)
8117 {
8118 /* Isolate one termcap name */
8119 for (len = 0; arg[off + len] &&
8120 arg[off + len] != ','; ++len)
8121 ;
8122 tname = vim_strnsave(arg + off, len);
8123 if (tname == NULL) /* out of memory */
8124 {
8125 error = TRUE;
8126 break;
8127 }
8128 /* lookup the escape sequence for the item */
8129 p = get_term_code(tname);
8130 vim_free(tname);
8131 if (p == NULL) /* ignore non-existing things */
8132 p = (char_u *)"";
8133
8134 /* Append it to the already found stuff */
8135 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
8136 {
8137 EMSG2(_("E422: terminal code too long: %s"), arg);
8138 error = TRUE;
8139 break;
8140 }
8141 STRCAT(buf, p);
8142
8143 /* Advance to the next item */
8144 off += len;
8145 if (arg[off] == ',') /* another one follows */
8146 ++off;
8147 }
8148 }
8149 else
8150 {
8151 /*
8152 * Copy characters from arg[] to buf[], translating <> codes.
8153 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008154 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00008155 {
Bram Moolenaar35a4cfa2016-08-14 16:07:48 +02008156 len = trans_special(&p, buf + off, FALSE, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008157 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008158 off += len;
8159 else /* copy as normal char */
8160 buf[off++] = *p++;
8161 }
8162 buf[off] = NUL;
8163 }
8164 if (error)
8165 break;
8166
8167 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
8168 p = NULL;
8169 else
8170 p = vim_strsave(buf);
8171 if (key[2] == 'A')
8172 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008173 vim_free(HL_TABLE()[idx].sg_start);
8174 HL_TABLE()[idx].sg_start = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008175 }
8176 else
8177 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008178 vim_free(HL_TABLE()[idx].sg_stop);
8179 HL_TABLE()[idx].sg_stop = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008180 }
8181 }
8182 else
8183 {
8184 EMSG2(_("E423: Illegal argument: %s"), key_start);
8185 error = TRUE;
8186 break;
8187 }
Bram Moolenaar414168d2017-09-10 15:21:55 +02008188 HL_TABLE()[idx].sg_cleared = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008189
8190 /*
8191 * When highlighting has been given for a group, don't link it.
8192 */
Bram Moolenaar414168d2017-09-10 15:21:55 +02008193 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8194 HL_TABLE()[idx].sg_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008195
8196 /*
8197 * Continue with next argument.
8198 */
8199 linep = skipwhite(linep);
8200 }
8201
8202 /*
8203 * If there is an error, and it's a new entry, remove it from the table.
8204 */
8205 if (error && idx == highlight_ga.ga_len)
8206 syn_unadd_group();
8207 else
8208 {
8209 if (is_normal_group)
8210 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008211 HL_TABLE()[idx].sg_term_attr = 0;
8212 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008213#ifdef FEAT_GUI
Bram Moolenaar414168d2017-09-10 15:21:55 +02008214 HL_TABLE()[idx].sg_gui_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008215 /*
8216 * Need to update all groups, because they might be using "bg"
8217 * and/or "fg", which have been changed now.
8218 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008219#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008220#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008221 if (USE_24BIT)
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008222 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008223 highlight_gui_started();
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008224 did_highlight_changed = TRUE;
8225 redraw_all_later(NOT_VALID);
8226 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008227#endif
8228 }
8229#ifdef FEAT_GUI_X11
8230# ifdef FEAT_MENU
8231 else if (is_menu_group)
8232 {
8233 if (gui.in_use && do_colors)
8234 gui_mch_new_menu_colors();
8235 }
8236# endif
8237 else if (is_scrollbar_group)
8238 {
8239 if (gui.in_use && do_colors)
8240 gui_new_scrollbar_colors();
8241 }
8242# ifdef FEAT_BEVAL
8243 else if (is_tooltip_group)
8244 {
8245 if (gui.in_use && do_colors)
8246 gui_mch_new_tooltip_colors();
8247 }
8248# endif
8249#endif
8250 else
8251 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008252#ifdef FEAT_EVAL
Bram Moolenaar414168d2017-09-10 15:21:55 +02008253 HL_TABLE()[idx].sg_scriptID = current_SID;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008254#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008255 }
Bram Moolenaar99433292017-09-08 12:37:47 +02008256
Bram Moolenaar071d4272004-06-13 20:20:40 +00008257 vim_free(key);
8258 vim_free(arg);
8259
Bram Moolenaar99433292017-09-08 12:37:47 +02008260 /* Only call highlight_changed() once, after a sequence of highlight
8261 * commands, and only if an attribute actually changed. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02008262 if (memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008263#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
8264 && !did_highlight_changed
8265#endif
8266 )
Bram Moolenaar99433292017-09-08 12:37:47 +02008267 {
8268 redraw_all_later(NOT_VALID);
8269 need_highlight_changed = TRUE;
8270 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008271}
8272
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008273#if defined(EXITFREE) || defined(PROTO)
8274 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008275free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008276{
8277 int i;
8278
8279 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008280 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008281 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008282 vim_free(HL_TABLE()[i].sg_name);
8283 vim_free(HL_TABLE()[i].sg_name_u);
8284 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008285 ga_clear(&highlight_ga);
8286}
8287#endif
8288
Bram Moolenaar071d4272004-06-13 20:20:40 +00008289/*
8290 * Reset the cterm colors to what they were before Vim was started, if
8291 * possible. Otherwise reset them to zero.
8292 */
8293 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008294restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008295{
Bram Moolenaar48e330a2016-02-23 14:53:34 +01008296#if defined(WIN3264) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008297 /* Since t_me has been set, this probably means that the user
8298 * wants to use this as default colors. Need to reset default
8299 * background/foreground colors. */
8300 mch_set_normal_colors();
8301#else
8302 cterm_normal_fg_color = 0;
8303 cterm_normal_fg_bold = 0;
8304 cterm_normal_bg_color = 0;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008305# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008306 cterm_normal_fg_gui_color = INVALCOLOR;
8307 cterm_normal_bg_gui_color = INVALCOLOR;
8308# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008309#endif
8310}
8311
8312/*
8313 * Return TRUE if highlight group "idx" has any settings.
8314 * When "check_link" is TRUE also check for an existing link.
8315 */
8316 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008317hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008318{
8319 return ( HL_TABLE()[idx].sg_term_attr != 0
8320 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008321 || HL_TABLE()[idx].sg_cterm_fg != 0
8322 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008323#ifdef FEAT_GUI
8324 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008325 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8326 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8327 || HL_TABLE()[idx].sg_gui_sp_name != NULL
Bram Moolenaar87748452017-03-12 17:10:33 +01008328 || HL_TABLE()[idx].sg_font_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008329#endif
8330 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8331}
8332
8333/*
8334 * Clear highlighting for one group.
8335 */
8336 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008337highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008338{
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008339 HL_TABLE()[idx].sg_cleared = TRUE;
8340
Bram Moolenaar071d4272004-06-13 20:20:40 +00008341 HL_TABLE()[idx].sg_term = 0;
8342 vim_free(HL_TABLE()[idx].sg_start);
8343 HL_TABLE()[idx].sg_start = NULL;
8344 vim_free(HL_TABLE()[idx].sg_stop);
8345 HL_TABLE()[idx].sg_stop = NULL;
8346 HL_TABLE()[idx].sg_term_attr = 0;
8347 HL_TABLE()[idx].sg_cterm = 0;
8348 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8349 HL_TABLE()[idx].sg_cterm_fg = 0;
8350 HL_TABLE()[idx].sg_cterm_bg = 0;
8351 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008352#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008353 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008354 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8355 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008356 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8357 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008358 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8359 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008360#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008361#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008362 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8363 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008364#endif
8365#ifdef FEAT_GUI
Bram Moolenaar61623362010-07-14 22:04:22 +02008366 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008367 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8368 HL_TABLE()[idx].sg_font = NOFONT;
8369# ifdef FEAT_XFONTSET
8370 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8371 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8372# endif
8373 vim_free(HL_TABLE()[idx].sg_font_name);
8374 HL_TABLE()[idx].sg_font_name = NULL;
8375 HL_TABLE()[idx].sg_gui_attr = 0;
8376#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008377#ifdef FEAT_EVAL
8378 /* Clear the script ID only when there is no link, since that is not
8379 * cleared. */
8380 if (HL_TABLE()[idx].sg_link == 0)
8381 HL_TABLE()[idx].sg_scriptID = 0;
8382#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008383}
8384
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008385#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008386/*
8387 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008388 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008389 * "Tooltip" colors.
8390 */
8391 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008392set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008393{
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008394#ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008395# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008396 if (gui.in_use)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008397# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008398 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008399 if (set_group_colors((char_u *)"Normal",
8400 &gui.norm_pixel, &gui.back_pixel,
8401 FALSE, TRUE, FALSE))
8402 {
8403 gui_mch_new_colors();
8404 must_redraw = CLEAR;
8405 }
8406# ifdef FEAT_GUI_X11
8407 if (set_group_colors((char_u *)"Menu",
8408 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8409 TRUE, FALSE, FALSE))
8410 {
8411# ifdef FEAT_MENU
8412 gui_mch_new_menu_colors();
8413# endif
8414 must_redraw = CLEAR;
8415 }
8416# ifdef FEAT_BEVAL
8417 if (set_group_colors((char_u *)"Tooltip",
8418 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8419 FALSE, FALSE, TRUE))
8420 {
8421# ifdef FEAT_TOOLBAR
8422 gui_mch_new_tooltip_colors();
8423# endif
8424 must_redraw = CLEAR;
8425 }
8426# endif
8427 if (set_group_colors((char_u *)"Scrollbar",
8428 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8429 FALSE, FALSE, FALSE))
8430 {
8431 gui_new_scrollbar_colors();
8432 must_redraw = CLEAR;
8433 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008434# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008435 }
8436#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008437#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008438# ifdef FEAT_GUI
8439 else
8440# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008441 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008442 int idx;
8443
8444 idx = syn_name2id((char_u *)"Normal") - 1;
8445 if (idx >= 0)
8446 {
8447 gui_do_one_color(idx, FALSE, FALSE);
8448
8449 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8450 {
8451 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
8452 must_redraw = CLEAR;
8453 }
8454 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8455 {
8456 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
8457 must_redraw = CLEAR;
8458 }
8459 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008460 }
8461#endif
8462}
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008463#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008464
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008465#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008466/*
8467 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8468 */
8469 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008470set_group_colors(
8471 char_u *name,
8472 guicolor_T *fgp,
8473 guicolor_T *bgp,
8474 int do_menu,
8475 int use_norm,
8476 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008477{
8478 int idx;
8479
8480 idx = syn_name2id(name) - 1;
8481 if (idx >= 0)
8482 {
8483 gui_do_one_color(idx, do_menu, do_tooltip);
8484
8485 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8486 *fgp = HL_TABLE()[idx].sg_gui_fg;
8487 else if (use_norm)
8488 *fgp = gui.def_norm_pixel;
8489 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8490 *bgp = HL_TABLE()[idx].sg_gui_bg;
8491 else if (use_norm)
8492 *bgp = gui.def_back_pixel;
8493 return TRUE;
8494 }
8495 return FALSE;
8496}
8497
8498/*
8499 * Get the font of the "Normal" group.
8500 * Returns "" when it's not found or not set.
8501 */
8502 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008503hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008504{
8505 int id;
8506 char_u *s;
8507
8508 id = syn_name2id((char_u *)"Normal");
8509 if (id > 0)
8510 {
8511 s = HL_TABLE()[id - 1].sg_font_name;
8512 if (s != NULL)
8513 return s;
8514 }
8515 return (char_u *)"";
8516}
8517
8518/*
8519 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8520 * actually chosen to be used.
8521 */
8522 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008523hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008524{
8525 int id;
8526
8527 id = syn_name2id((char_u *)"Normal");
8528 if (id > 0)
8529 {
8530 vim_free(HL_TABLE()[id - 1].sg_font_name);
8531 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8532 }
8533}
8534
8535/*
8536 * Set background color for "Normal" group. Called by gui_set_bg_color()
8537 * when the color is known.
8538 */
8539 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008540hl_set_bg_color_name(
8541 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008542{
8543 int id;
8544
8545 if (name != NULL)
8546 {
8547 id = syn_name2id((char_u *)"Normal");
8548 if (id > 0)
8549 {
8550 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8551 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8552 }
8553 }
8554}
8555
8556/*
8557 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8558 * when the color is known.
8559 */
8560 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008561hl_set_fg_color_name(
8562 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008563{
8564 int id;
8565
8566 if (name != NULL)
8567 {
8568 id = syn_name2id((char_u *)"Normal");
8569 if (id > 0)
8570 {
8571 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8572 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8573 }
8574 }
8575}
8576
8577/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008578 * Return the handle for a font name.
8579 * Returns NOFONT when failed.
8580 */
8581 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008582font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008583{
8584 if (STRCMP(name, "NONE") == 0)
8585 return NOFONT;
8586
8587 return gui_mch_get_font(name, TRUE);
8588}
8589
8590# ifdef FEAT_XFONTSET
8591/*
8592 * Return the handle for a fontset name.
8593 * Returns NOFONTSET when failed.
8594 */
8595 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008596fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008597{
8598 if (STRCMP(name, "NONE") == 0)
8599 return NOFONTSET;
8600
8601 return gui_mch_get_fontset(name, TRUE, fixed_width);
8602}
8603# endif
8604
8605/*
8606 * Get the font or fontset for one highlight group.
8607 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008608 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008609hl_do_font(
8610 int idx,
8611 char_u *arg,
8612 int do_normal, /* set normal font */
8613 int do_menu UNUSED, /* set menu font */
8614 int do_tooltip UNUSED, /* set tooltip font */
8615 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008616{
8617# ifdef FEAT_XFONTSET
8618 /* If 'guifontset' is not empty, first try using the name as a
8619 * fontset. If that doesn't work, use it as a font name. */
8620 if (*p_guifontset != NUL
8621# ifdef FONTSET_ALWAYS
8622 || do_menu
8623# endif
8624# ifdef FEAT_BEVAL_TIP
8625 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8626 || do_tooltip
8627# endif
8628 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008629 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008630 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008631 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008632 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8633# ifdef FONTSET_ALWAYS
8634 || do_menu
8635# endif
8636# ifdef FEAT_BEVAL_TIP
8637 || do_tooltip
8638# endif
8639 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008640 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008641 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8642 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008643 /* If it worked and it's the Normal group, use it as the normal
8644 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008645 if (do_normal)
8646 gui_init_font(arg, TRUE);
8647# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8648 if (do_menu)
8649 {
8650# ifdef FONTSET_ALWAYS
8651 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8652# else
8653 /* YIKES! This is a bug waiting to crash the program */
8654 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8655# endif
8656 gui_mch_new_menu_font();
8657 }
8658# ifdef FEAT_BEVAL
8659 if (do_tooltip)
8660 {
8661 /* The Athena widget set cannot currently handle switching between
8662 * displaying a single font and a fontset.
8663 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008664 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008665 * XFontStruct is used.
8666 */
8667 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8668 gui_mch_new_tooltip_font();
8669 }
8670# endif
8671# endif
8672 }
8673 else
8674# endif
8675 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008676 if (free_font)
8677 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008678 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8679 /* If it worked and it's the Normal group, use it as the
8680 * normal font. Same for the Menu group. */
8681 if (HL_TABLE()[idx].sg_font != NOFONT)
8682 {
8683 if (do_normal)
8684 gui_init_font(arg, FALSE);
8685#ifndef FONTSET_ALWAYS
8686# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8687 if (do_menu)
8688 {
8689 gui.menu_font = HL_TABLE()[idx].sg_font;
8690 gui_mch_new_menu_font();
8691 }
8692# endif
8693#endif
8694 }
8695 }
8696}
8697
8698#endif /* FEAT_GUI */
8699
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008700#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008701/*
8702 * Return the handle for a color name.
8703 * Returns INVALCOLOR when failed.
8704 */
Bram Moolenaar3d9bdfe2017-08-12 22:55:58 +02008705 guicolor_T
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008706color_name2handle(char_u *name)
8707{
8708 if (STRCMP(name, "NONE") == 0)
8709 return INVALCOLOR;
8710
8711 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8712 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008713#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008714 if (gui.in_use)
8715#endif
8716#ifdef FEAT_GUI
8717 return gui.norm_pixel;
8718#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008719#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008720 if (cterm_normal_fg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008721 return cterm_normal_fg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008722 /* Guess that the foreground is black or white. */
8723 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008724#endif
8725 }
8726 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8727 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008728#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008729 if (gui.in_use)
8730#endif
8731#ifdef FEAT_GUI
8732 return gui.back_pixel;
8733#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008734#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008735 if (cterm_normal_bg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008736 return cterm_normal_bg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008737 /* Guess that the background is white or black. */
8738 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008739#endif
8740 }
8741
8742 return GUI_GET_COLOR(name);
8743}
8744#endif
8745
Bram Moolenaar071d4272004-06-13 20:20:40 +00008746/*
8747 * Table with the specifications for an attribute number.
8748 * Note that this table is used by ALL buffers. This is required because the
8749 * GUI can redraw at any time for any buffer.
8750 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008751static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008752
8753#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8754
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008755static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008756
8757#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8758
8759#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008760static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008761
8762#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8763#endif
8764
8765/*
8766 * Return the attr number for a set of colors and font.
8767 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8768 * if the combination is new.
8769 * Return 0 for error (no more room).
8770 */
8771 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008772get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008773{
8774 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008775 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008776 static int recursive = FALSE;
8777
8778 /*
8779 * Init the table, in case it wasn't done yet.
8780 */
8781 table->ga_itemsize = sizeof(attrentry_T);
8782 table->ga_growsize = 7;
8783
8784 /*
8785 * Try to find an entry with the same specifications.
8786 */
8787 for (i = 0; i < table->ga_len; ++i)
8788 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008789 taep = &(((attrentry_T *)table->ga_data)[i]);
8790 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008791 && (
8792#ifdef FEAT_GUI
8793 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008794 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8795 && aep->ae_u.gui.bg_color
8796 == taep->ae_u.gui.bg_color
8797 && aep->ae_u.gui.sp_color
8798 == taep->ae_u.gui.sp_color
8799 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008800# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008801 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008802# endif
8803 ))
8804 ||
8805#endif
8806 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008807 && (aep->ae_u.term.start == NULL)
8808 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008809 && (aep->ae_u.term.start == NULL
8810 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008811 taep->ae_u.term.start) == 0)
8812 && (aep->ae_u.term.stop == NULL)
8813 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008814 && (aep->ae_u.term.stop == NULL
8815 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008816 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008817 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008818 && aep->ae_u.cterm.fg_color
8819 == taep->ae_u.cterm.fg_color
8820 && aep->ae_u.cterm.bg_color
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008821 == taep->ae_u.cterm.bg_color
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008822#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008823 && aep->ae_u.cterm.fg_rgb
8824 == taep->ae_u.cterm.fg_rgb
8825 && aep->ae_u.cterm.bg_rgb
8826 == taep->ae_u.cterm.bg_rgb
8827#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008828 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008829
8830 return i + ATTR_OFF;
8831 }
8832
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008833 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008834 {
8835 /*
8836 * Running out of attribute entries! remove all attributes, and
8837 * compute new ones for all groups.
8838 * When called recursively, we are really out of numbers.
8839 */
8840 if (recursive)
8841 {
8842 EMSG(_("E424: Too many different highlighting attributes in use"));
8843 return 0;
8844 }
8845 recursive = TRUE;
8846
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008847 clear_hl_tables();
8848
Bram Moolenaar071d4272004-06-13 20:20:40 +00008849 must_redraw = CLEAR;
8850
8851 for (i = 0; i < highlight_ga.ga_len; ++i)
8852 set_hl_attr(i);
8853
8854 recursive = FALSE;
8855 }
8856
8857 /*
8858 * This is a new combination of colors and font, add an entry.
8859 */
8860 if (ga_grow(table, 1) == FAIL)
8861 return 0;
8862
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008863 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8864 vim_memset(taep, 0, sizeof(attrentry_T));
8865 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008866#ifdef FEAT_GUI
8867 if (table == &gui_attr_table)
8868 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008869 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8870 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8871 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8872 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008873# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008874 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008875# endif
8876 }
8877#endif
8878 if (table == &term_attr_table)
8879 {
8880 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008881 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008882 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008883 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008884 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008885 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008886 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008887 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008888 }
8889 else if (table == &cterm_attr_table)
8890 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008891 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8892 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008893#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008894 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
8895 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
8896#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008897 }
8898 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008899 return (table->ga_len - 1 + ATTR_OFF);
8900}
8901
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008902/*
Bram Moolenaareeac6772017-07-23 15:48:37 +02008903 * Get an attribute index for a cterm entry.
8904 * Uses an existing entry when possible or adds one when needed.
8905 */
8906 int
8907get_cterm_attr_idx(int attr, int fg, int bg)
8908{
8909 attrentry_T at_en;
8910
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008911 vim_memset(&at_en, 0, sizeof(attrentry_T));
Bram Moolenaareeac6772017-07-23 15:48:37 +02008912 at_en.ae_attr = attr;
8913 at_en.ae_u.cterm.fg_color = fg;
8914 at_en.ae_u.cterm.bg_color = bg;
8915 return get_attr_entry(&cterm_attr_table, &at_en);
8916}
8917
Bram Moolenaar065f41c2017-07-23 18:07:56 +02008918#if defined(FEAT_TERMGUICOLORS) || defined(PROTO)
8919/*
8920 * Get an attribute index for a 'termguicolors' entry.
8921 * Uses an existing entry when possible or adds one when needed.
8922 */
8923 int
8924get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8925{
8926 attrentry_T at_en;
8927
8928 vim_memset(&at_en, 0, sizeof(attrentry_T));
8929 at_en.ae_attr = attr;
8930 at_en.ae_u.cterm.fg_rgb = fg;
8931 at_en.ae_u.cterm.bg_rgb = bg;
8932 return get_attr_entry(&cterm_attr_table, &at_en);
8933}
8934#endif
8935
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008936#if defined(FEAT_GUI) || defined(PROTO)
8937/*
8938 * Get an attribute index for a cterm entry.
8939 * Uses an existing entry when possible or adds one when needed.
8940 */
8941 int
8942get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8943{
8944 attrentry_T at_en;
8945
8946 vim_memset(&at_en, 0, sizeof(attrentry_T));
8947 at_en.ae_attr = attr;
8948 at_en.ae_u.gui.fg_color = fg;
8949 at_en.ae_u.gui.bg_color = bg;
8950 return get_attr_entry(&gui_attr_table, &at_en);
8951}
8952#endif
8953
Bram Moolenaareeac6772017-07-23 15:48:37 +02008954/*
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008955 * Clear all highlight tables.
8956 */
8957 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008958clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008959{
8960 int i;
8961 attrentry_T *taep;
8962
8963#ifdef FEAT_GUI
8964 ga_clear(&gui_attr_table);
8965#endif
8966 for (i = 0; i < term_attr_table.ga_len; ++i)
8967 {
8968 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8969 vim_free(taep->ae_u.term.start);
8970 vim_free(taep->ae_u.term.stop);
8971 }
8972 ga_clear(&term_attr_table);
8973 ga_clear(&cterm_attr_table);
8974}
8975
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008976#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008977/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008978 * Combine special attributes (e.g., for spelling) with other attributes
8979 * (e.g., for syntax highlighting).
8980 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008981 * This creates a new group when required.
8982 * Since we expect there to be few spelling mistakes we don't cache the
8983 * result.
8984 * Return the resulting attributes.
8985 */
8986 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008987hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008988{
8989 attrentry_T *char_aep = NULL;
8990 attrentry_T *spell_aep;
8991 attrentry_T new_en;
8992
8993 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008994 return prim_attr;
8995 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02008996 return ATTR_COMBINE(char_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008997#ifdef FEAT_GUI
8998 if (gui.in_use)
8999 {
9000 if (char_attr > HL_ALL)
9001 char_aep = syn_gui_attr2entry(char_attr);
9002 if (char_aep != NULL)
9003 new_en = *char_aep;
9004 else
9005 {
9006 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00009007 new_en.ae_u.gui.fg_color = INVALCOLOR;
9008 new_en.ae_u.gui.bg_color = INVALCOLOR;
9009 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00009010 if (char_attr <= HL_ALL)
9011 new_en.ae_attr = char_attr;
9012 }
9013
Bram Moolenaar30abd282005-06-22 22:35:10 +00009014 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009015 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009016 else
9017 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00009018 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009019 if (spell_aep != NULL)
9020 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009021 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
9022 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009023 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
9024 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
9025 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
9026 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
9027 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
9028 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
9029 if (spell_aep->ae_u.gui.font != NOFONT)
9030 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
9031# ifdef FEAT_XFONTSET
9032 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
9033 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
9034# endif
9035 }
9036 }
9037 return get_attr_entry(&gui_attr_table, &new_en);
9038 }
9039#endif
9040
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009041 if (IS_CTERM)
Bram Moolenaar217ad922005-03-20 22:37:15 +00009042 {
9043 if (char_attr > HL_ALL)
9044 char_aep = syn_cterm_attr2entry(char_attr);
9045 if (char_aep != NULL)
9046 new_en = *char_aep;
9047 else
9048 {
9049 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaar0cdb72a2017-01-02 21:37:40 +01009050#ifdef FEAT_TERMGUICOLORS
9051 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
9052 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
9053#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00009054 if (char_attr <= HL_ALL)
9055 new_en.ae_attr = char_attr;
9056 }
9057
Bram Moolenaar30abd282005-06-22 22:35:10 +00009058 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009059 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009060 else
9061 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00009062 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009063 if (spell_aep != NULL)
9064 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009065 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
9066 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009067 if (spell_aep->ae_u.cterm.fg_color > 0)
9068 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
9069 if (spell_aep->ae_u.cterm.bg_color > 0)
9070 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009071#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009072 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009073 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009074 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009075 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
9076#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00009077 }
9078 }
9079 return get_attr_entry(&cterm_attr_table, &new_en);
9080 }
9081
9082 if (char_attr > HL_ALL)
9083 char_aep = syn_term_attr2entry(char_attr);
9084 if (char_aep != NULL)
9085 new_en = *char_aep;
9086 else
9087 {
9088 vim_memset(&new_en, 0, sizeof(new_en));
9089 if (char_attr <= HL_ALL)
9090 new_en.ae_attr = char_attr;
9091 }
9092
Bram Moolenaar30abd282005-06-22 22:35:10 +00009093 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009094 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009095 else
9096 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00009097 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009098 if (spell_aep != NULL)
9099 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009100 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009101 if (spell_aep->ae_u.term.start != NULL)
9102 {
9103 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
9104 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
9105 }
9106 }
9107 }
9108 return get_attr_entry(&term_attr_table, &new_en);
9109}
9110#endif
9111
Bram Moolenaar071d4272004-06-13 20:20:40 +00009112#ifdef FEAT_GUI
9113
9114 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009115syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009116{
9117 attr -= ATTR_OFF;
9118 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
9119 return NULL;
9120 return &(GUI_ATTR_ENTRY(attr));
9121}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009122#endif /* FEAT_GUI */
9123
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009124/*
9125 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
9126 * Only to be used when "attr" > HL_ALL.
9127 */
9128 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009129syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009130{
9131 attrentry_T *aep;
9132
9133#ifdef FEAT_GUI
9134 if (gui.in_use)
9135 aep = syn_gui_attr2entry(attr);
9136 else
9137#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009138 if (IS_CTERM)
9139 aep = syn_cterm_attr2entry(attr);
9140 else
9141 aep = syn_term_attr2entry(attr);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009142
9143 if (aep == NULL) /* highlighting not set */
9144 return 0;
9145 return aep->ae_attr;
9146}
9147
9148
Bram Moolenaar071d4272004-06-13 20:20:40 +00009149 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009150syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009151{
9152 attr -= ATTR_OFF;
9153 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
9154 return NULL;
9155 return &(TERM_ATTR_ENTRY(attr));
9156}
9157
9158 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009159syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009160{
9161 attr -= ATTR_OFF;
9162 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
9163 return NULL;
9164 return &(CTERM_ATTR_ENTRY(attr));
9165}
9166
9167#define LIST_ATTR 1
9168#define LIST_STRING 2
9169#define LIST_INT 3
9170
9171 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009172highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009173{
9174 struct hl_group *sgp;
9175 int didh = FALSE;
9176
9177 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
9178
9179 didh = highlight_list_arg(id, didh, LIST_ATTR,
9180 sgp->sg_term, NULL, "term");
9181 didh = highlight_list_arg(id, didh, LIST_STRING,
9182 0, sgp->sg_start, "start");
9183 didh = highlight_list_arg(id, didh, LIST_STRING,
9184 0, sgp->sg_stop, "stop");
9185
9186 didh = highlight_list_arg(id, didh, LIST_ATTR,
9187 sgp->sg_cterm, NULL, "cterm");
9188 didh = highlight_list_arg(id, didh, LIST_INT,
9189 sgp->sg_cterm_fg, NULL, "ctermfg");
9190 didh = highlight_list_arg(id, didh, LIST_INT,
9191 sgp->sg_cterm_bg, NULL, "ctermbg");
9192
Bram Moolenaar61623362010-07-14 22:04:22 +02009193#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009194 didh = highlight_list_arg(id, didh, LIST_ATTR,
9195 sgp->sg_gui, NULL, "gui");
9196 didh = highlight_list_arg(id, didh, LIST_STRING,
9197 0, sgp->sg_gui_fg_name, "guifg");
9198 didh = highlight_list_arg(id, didh, LIST_STRING,
9199 0, sgp->sg_gui_bg_name, "guibg");
9200 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009201 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02009202#endif
9203#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009204 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00009205 0, sgp->sg_font_name, "font");
9206#endif
9207
Bram Moolenaar661b1822005-07-28 22:36:45 +00009208 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009209 {
9210 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00009211 didh = TRUE;
Bram Moolenaar8820b482017-03-16 17:23:31 +01009212 msg_puts_attr((char_u *)"links to", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009213 msg_putchar(' ');
9214 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
9215 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009216
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009217 if (!didh)
9218 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00009219#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009220 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009221 last_set_msg(sgp->sg_scriptID);
9222#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009223}
9224
9225 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009226highlight_list_arg(
9227 int id,
9228 int didh,
9229 int type,
9230 int iarg,
9231 char_u *sarg,
9232 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009233{
9234 char_u buf[100];
9235 char_u *ts;
9236 int i;
9237
Bram Moolenaar661b1822005-07-28 22:36:45 +00009238 if (got_int)
9239 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009240 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
9241 {
9242 ts = buf;
9243 if (type == LIST_INT)
9244 sprintf((char *)buf, "%d", iarg - 1);
9245 else if (type == LIST_STRING)
9246 ts = sarg;
9247 else /* type == LIST_ATTR */
9248 {
9249 buf[0] = NUL;
9250 for (i = 0; hl_attr_table[i] != 0; ++i)
9251 {
9252 if (iarg & hl_attr_table[i])
9253 {
9254 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02009255 vim_strcat(buf, (char_u *)",", 100);
9256 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009257 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
9258 }
9259 }
9260 }
9261
9262 (void)syn_list_header(didh,
9263 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9264 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00009265 if (!got_int)
9266 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009267 if (*name != NUL)
9268 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01009269 MSG_PUTS_ATTR(name, HL_ATTR(HLF_D));
9270 MSG_PUTS_ATTR("=", HL_ATTR(HLF_D));
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009271 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009272 msg_outtrans(ts);
9273 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009274 }
9275 return didh;
9276}
9277
9278#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9279/*
9280 * Return "1" if highlight group "id" has attribute "flag".
9281 * Return NULL otherwise.
9282 */
9283 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009284highlight_has_attr(
9285 int id,
9286 int flag,
9287 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009288{
9289 int attr;
9290
9291 if (id <= 0 || id > highlight_ga.ga_len)
9292 return NULL;
9293
Bram Moolenaar61623362010-07-14 22:04:22 +02009294#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009295 if (modec == 'g')
9296 attr = HL_TABLE()[id - 1].sg_gui;
9297 else
9298#endif
9299 if (modec == 'c')
9300 attr = HL_TABLE()[id - 1].sg_cterm;
9301 else
9302 attr = HL_TABLE()[id - 1].sg_term;
9303
9304 if (attr & flag)
9305 return (char_u *)"1";
9306 return NULL;
9307}
9308#endif
9309
9310#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9311/*
9312 * Return color name of highlight group "id".
9313 */
9314 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009315highlight_color(
9316 int id,
9317 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
9318 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009319{
9320 static char_u name[20];
9321 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009322 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009323 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009324 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009325
9326 if (id <= 0 || id > highlight_ga.ga_len)
9327 return NULL;
9328
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009329 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009330 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009331 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02009332 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009333 font = TRUE;
9334 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009335 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009336 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9337 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009338 if (modec == 'g')
9339 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009340# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009341# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009342 /* return font name */
9343 if (font)
9344 return HL_TABLE()[id - 1].sg_font_name;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009345# endif
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009346
Bram Moolenaar071d4272004-06-13 20:20:40 +00009347 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009348 if ((USE_24BIT) && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009349 {
9350 guicolor_T color;
9351 long_u rgb;
9352 static char_u buf[10];
9353
9354 if (fg)
9355 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009356 else if (sp)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009357# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009358 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009359# else
9360 color = INVALCOLOR;
9361# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009362 else
9363 color = HL_TABLE()[id - 1].sg_gui_bg;
9364 if (color == INVALCOLOR)
9365 return NULL;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009366 rgb = (long_u)GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009367 sprintf((char *)buf, "#%02x%02x%02x",
9368 (unsigned)(rgb >> 16),
9369 (unsigned)(rgb >> 8) & 255,
9370 (unsigned)rgb & 255);
9371 return buf;
9372 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009373# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009374 if (fg)
9375 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009376 if (sp)
9377 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009378 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9379 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009380 if (font || sp)
9381 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009382 if (modec == 'c')
9383 {
9384 if (fg)
9385 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9386 else
9387 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009388 if (n < 0)
9389 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009390 sprintf((char *)name, "%d", n);
9391 return name;
9392 }
9393 /* term doesn't have color */
9394 return NULL;
9395}
9396#endif
9397
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009398#if (defined(FEAT_SYN_HL) \
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009399 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009400 && defined(FEAT_PRINTER)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009401/*
9402 * Return color name of highlight group "id" as RGB value.
9403 */
9404 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009405highlight_gui_color_rgb(
9406 int id,
9407 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009408{
9409 guicolor_T color;
9410
9411 if (id <= 0 || id > highlight_ga.ga_len)
9412 return 0L;
9413
9414 if (fg)
9415 color = HL_TABLE()[id - 1].sg_gui_fg;
9416 else
9417 color = HL_TABLE()[id - 1].sg_gui_bg;
9418
9419 if (color == INVALCOLOR)
9420 return 0L;
9421
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009422 return GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009423}
9424#endif
9425
9426/*
9427 * Output the syntax list header.
9428 * Return TRUE when started a new line.
9429 */
9430 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009431syn_list_header(
9432 int did_header, /* did header already */
9433 int outlen, /* length of string that comes */
9434 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009435{
9436 int endcol = 19;
9437 int newline = TRUE;
9438
9439 if (!did_header)
9440 {
9441 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009442 if (got_int)
9443 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009444 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9445 endcol = 15;
9446 }
9447 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009448 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009449 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009450 if (got_int)
9451 return TRUE;
9452 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009453 else
9454 {
9455 if (msg_col >= endcol) /* wrap around is like starting a new line */
9456 newline = FALSE;
9457 }
9458
9459 if (msg_col >= endcol) /* output at least one space */
9460 endcol = msg_col + 1;
9461 if (Columns <= endcol) /* avoid hang for tiny window */
9462 endcol = Columns - 1;
9463
9464 msg_advance(endcol);
9465
9466 /* Show "xxx" with the attributes. */
9467 if (!did_header)
9468 {
9469 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9470 msg_putchar(' ');
9471 }
9472
9473 return newline;
9474}
9475
9476/*
9477 * Set the attribute numbers for a highlight group.
9478 * Called after one of the attributes has changed.
9479 */
9480 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009481set_hl_attr(
9482 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009483{
9484 attrentry_T at_en;
9485 struct hl_group *sgp = HL_TABLE() + idx;
9486
9487 /* The "Normal" group doesn't need an attribute number */
9488 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9489 return;
9490
9491#ifdef FEAT_GUI
9492 /*
9493 * For the GUI mode: If there are other than "normal" highlighting
9494 * attributes, need to allocate an attr number.
9495 */
9496 if (sgp->sg_gui_fg == INVALCOLOR
9497 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009498 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009499 && sgp->sg_font == NOFONT
9500# ifdef FEAT_XFONTSET
9501 && sgp->sg_fontset == NOFONTSET
9502# endif
9503 )
9504 {
9505 sgp->sg_gui_attr = sgp->sg_gui;
9506 }
9507 else
9508 {
9509 at_en.ae_attr = sgp->sg_gui;
9510 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9511 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009512 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009513 at_en.ae_u.gui.font = sgp->sg_font;
9514# ifdef FEAT_XFONTSET
9515 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9516# endif
9517 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9518 }
9519#endif
9520 /*
9521 * For the term mode: If there are other than "normal" highlighting
9522 * attributes, need to allocate an attr number.
9523 */
9524 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9525 sgp->sg_term_attr = sgp->sg_term;
9526 else
9527 {
9528 at_en.ae_attr = sgp->sg_term;
9529 at_en.ae_u.term.start = sgp->sg_start;
9530 at_en.ae_u.term.stop = sgp->sg_stop;
9531 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9532 }
9533
9534 /*
9535 * For the color term mode: If there are other than "normal"
9536 * highlighting attributes, need to allocate an attr number.
9537 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009538 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009539# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009540 && sgp->sg_gui_fg == INVALCOLOR
9541 && sgp->sg_gui_bg == INVALCOLOR
9542# endif
9543 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00009544 sgp->sg_cterm_attr = sgp->sg_cterm;
9545 else
9546 {
9547 at_en.ae_attr = sgp->sg_cterm;
9548 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9549 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009550# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar187147a2016-05-01 13:09:57 +02009551 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
9552 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009553# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009554 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9555 }
9556}
9557
9558/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009559 * Lookup a highlight group name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009560 * If it is not found, 0 is returned.
9561 */
9562 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009563syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009564{
9565 int i;
9566 char_u name_u[200];
9567
9568 /* Avoid using stricmp() too much, it's slow on some systems */
9569 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9570 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009571 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009572 vim_strup(name_u);
9573 for (i = highlight_ga.ga_len; --i >= 0; )
9574 if (HL_TABLE()[i].sg_name_u != NULL
9575 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9576 break;
9577 return i + 1;
9578}
9579
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02009580/*
9581 * Lookup a highlight group name and return its attributes.
9582 * Return zero if not found.
9583 */
9584 int
9585syn_name2attr(char_u *name)
9586{
9587 int id = syn_name2id(name);
9588
9589 if (id != 0)
Bram Moolenaar76301952017-09-22 13:53:37 +02009590 return syn_id2attr(id);
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02009591 return 0;
9592}
9593
Bram Moolenaar071d4272004-06-13 20:20:40 +00009594#if defined(FEAT_EVAL) || defined(PROTO)
9595/*
9596 * Return TRUE if highlight group "name" exists.
9597 */
9598 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009599highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009600{
9601 return (syn_name2id(name) > 0);
9602}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009603
9604# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9605/*
9606 * Return the name of highlight group "id".
9607 * When not a valid ID return an empty string.
9608 */
9609 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009610syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009611{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009612 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009613 return (char_u *)"";
9614 return HL_TABLE()[id - 1].sg_name;
9615}
9616# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009617#endif
9618
9619/*
9620 * Like syn_name2id(), but take a pointer + length argument.
9621 */
9622 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009623syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009624{
9625 char_u *name;
9626 int id = 0;
9627
9628 name = vim_strnsave(linep, len);
9629 if (name != NULL)
9630 {
9631 id = syn_name2id(name);
9632 vim_free(name);
9633 }
9634 return id;
9635}
9636
9637/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009638 * Find highlight group name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009639 * The argument is a pointer to the name and the length of the name.
9640 * If it doesn't exist yet, a new entry is created.
9641 * Return 0 for failure.
9642 */
9643 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009644syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009645{
9646 int id;
9647 char_u *name;
9648
9649 name = vim_strnsave(pp, len);
9650 if (name == NULL)
9651 return 0;
9652
9653 id = syn_name2id(name);
9654 if (id == 0) /* doesn't exist yet */
9655 id = syn_add_group(name);
9656 else
9657 vim_free(name);
9658 return id;
9659}
9660
9661/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009662 * Add new highlight group and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009663 * "name" must be an allocated string, it will be consumed.
9664 * Return 0 for failure.
9665 */
9666 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009667syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009668{
9669 char_u *p;
9670
9671 /* Check that the name is ASCII letters, digits and underscore. */
9672 for (p = name; *p != NUL; ++p)
9673 {
9674 if (!vim_isprintc(*p))
9675 {
9676 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009677 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009678 return 0;
9679 }
9680 else if (!ASCII_ISALNUM(*p) && *p != '_')
9681 {
9682 /* This is an error, but since there previously was no check only
9683 * give a warning. */
Bram Moolenaar8820b482017-03-16 17:23:31 +01009684 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009685 MSG(_("W18: Invalid character in group name"));
9686 break;
9687 }
9688 }
9689
9690 /*
9691 * First call for this growarray: init growing array.
9692 */
9693 if (highlight_ga.ga_data == NULL)
9694 {
9695 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9696 highlight_ga.ga_growsize = 10;
9697 }
9698
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009699 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009700 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009701 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009702 vim_free(name);
9703 return 0;
9704 }
9705
Bram Moolenaar071d4272004-06-13 20:20:40 +00009706 /*
9707 * Make room for at least one other syntax_highlight entry.
9708 */
9709 if (ga_grow(&highlight_ga, 1) == FAIL)
9710 {
9711 vim_free(name);
9712 return 0;
9713 }
9714
9715 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9716 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9717 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009718#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009719 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9720 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009721# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009722 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009723# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009724#endif
9725 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009726
9727 return highlight_ga.ga_len; /* ID is index plus one */
9728}
9729
9730/*
9731 * When, just after calling syn_add_group(), an error is discovered, this
9732 * function deletes the new name.
9733 */
9734 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009735syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009736{
9737 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009738 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9739 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9740}
9741
9742/*
9743 * Translate a group ID to highlight attributes.
9744 */
9745 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009746syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009747{
9748 int attr;
9749 struct hl_group *sgp;
9750
9751 hl_id = syn_get_final_id(hl_id);
9752 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9753
9754#ifdef FEAT_GUI
9755 /*
9756 * Only use GUI attr when the GUI is being used.
9757 */
9758 if (gui.in_use)
9759 attr = sgp->sg_gui_attr;
9760 else
9761#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009762 if (IS_CTERM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009763 attr = sgp->sg_cterm_attr;
9764 else
9765 attr = sgp->sg_term_attr;
9766
9767 return attr;
9768}
9769
Bram Moolenaarc71053c2017-09-14 00:00:44 +02009770#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009771/*
9772 * Get the GUI colors and attributes for a group ID.
9773 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9774 */
9775 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009776syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009777{
9778 struct hl_group *sgp;
9779
9780 hl_id = syn_get_final_id(hl_id);
9781 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9782
9783 *fgp = sgp->sg_gui_fg;
9784 *bgp = sgp->sg_gui_bg;
9785 return sgp->sg_gui;
9786}
9787#endif
9788
Bram Moolenaard371bbe2017-09-28 22:35:25 +02009789#if defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarc71053c2017-09-14 00:00:44 +02009790 void
9791syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
9792{
9793 struct hl_group *sgp;
9794
9795 hl_id = syn_get_final_id(hl_id);
9796 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9797 *fgp = sgp->sg_cterm_fg - 1;
9798 *bgp = sgp->sg_cterm_bg - 1;
9799}
9800#endif
9801
Bram Moolenaar071d4272004-06-13 20:20:40 +00009802/*
9803 * Translate a group ID to the final group ID (following links).
9804 */
9805 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009806syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009807{
9808 int count;
9809 struct hl_group *sgp;
9810
9811 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9812 return 0; /* Can be called from eval!! */
9813
9814 /*
9815 * Follow links until there is no more.
9816 * Look out for loops! Break after 100 links.
9817 */
9818 for (count = 100; --count >= 0; )
9819 {
9820 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9821 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9822 break;
9823 hl_id = sgp->sg_link;
9824 }
9825
9826 return hl_id;
9827}
9828
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009829#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009830/*
9831 * Call this function just after the GUI has started.
9832 * It finds the font and color handles for the highlighting groups.
9833 */
9834 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009835highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009836{
9837 int idx;
9838
9839 /* First get the colors from the "Normal" and "Menu" group, if set */
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009840# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
9841# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009842 if (USE_24BIT)
9843# endif
9844 set_normal_colors();
9845# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009846
9847 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9848 gui_do_one_color(idx, FALSE, FALSE);
9849
9850 highlight_changed();
9851}
9852
9853 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009854gui_do_one_color(
9855 int idx,
Bram Moolenaar380130f2016-04-22 11:24:43 +02009856 int do_menu UNUSED, /* TRUE: might set the menu font */
9857 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009858{
9859 int didit = FALSE;
9860
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009861# ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009862# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009863 if (gui.in_use)
9864# endif
9865 if (HL_TABLE()[idx].sg_font_name != NULL)
9866 {
9867 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009868 do_tooltip, TRUE);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009869 didit = TRUE;
9870 }
9871# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009872 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9873 {
9874 HL_TABLE()[idx].sg_gui_fg =
9875 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9876 didit = TRUE;
9877 }
9878 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9879 {
9880 HL_TABLE()[idx].sg_gui_bg =
9881 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9882 didit = TRUE;
9883 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009884# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009885 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9886 {
9887 HL_TABLE()[idx].sg_gui_sp =
9888 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9889 didit = TRUE;
9890 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009891# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009892 if (didit) /* need to get a new attr number */
9893 set_hl_attr(idx);
9894}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009895#endif
9896
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009897#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
9898/*
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02009899 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009900 */
9901 static void
9902combine_stl_hlt(
9903 int id,
9904 int id_S,
9905 int id_alt,
9906 int hlcnt,
9907 int i,
9908 int hlf,
9909 int *table)
9910{
9911 struct hl_group *hlt = HL_TABLE();
9912
9913 if (id_alt == 0)
9914 {
9915 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
9916 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
9917 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
9918# if defined(FEAT_GUI) || defined(FEAT_EVAL)
9919 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
9920# endif
9921 }
9922 else
9923 mch_memmove(&hlt[hlcnt + i],
9924 &hlt[id_alt - 1],
9925 sizeof(struct hl_group));
9926 hlt[hlcnt + i].sg_link = 0;
9927
9928 hlt[hlcnt + i].sg_term ^=
9929 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9930 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9931 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9932 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9933 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9934 hlt[hlcnt + i].sg_cterm ^=
9935 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9936 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9937 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9938 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9939 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
9940# if defined(FEAT_GUI) || defined(FEAT_EVAL)
9941 hlt[hlcnt + i].sg_gui ^=
9942 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
9943# endif
9944# ifdef FEAT_GUI
9945 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9946 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9947 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9948 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
9949 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9950 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
9951 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9952 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9953# ifdef FEAT_XFONTSET
9954 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9955 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9956# endif
9957# endif
9958 highlight_ga.ga_len = hlcnt + i + 1;
9959 set_hl_attr(hlcnt + i); /* At long last we can apply */
9960 table[i] = syn_id2attr(hlcnt + i + 1);
9961}
9962#endif
9963
Bram Moolenaar071d4272004-06-13 20:20:40 +00009964/*
9965 * Translate the 'highlight' option into attributes in highlight_attr[] and
9966 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9967 * corresponding highlights to use on top of HLF_SNC is computed.
9968 * Called only when the 'highlight' option has been changed and upon first
9969 * screen redraw after any :highlight command.
9970 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9971 */
9972 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009973highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009974{
9975 int hlf;
9976 int i;
9977 char_u *p;
9978 int attr;
9979 char_u *end;
9980 int id;
9981#ifdef USER_HIGHLIGHT
9982 char_u userhl[10];
9983# ifdef FEAT_STL_OPT
9984 int id_SNC = -1;
9985 int id_S = -1;
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009986# ifdef FEAT_TERMINAL
9987 int id_ST = -1;
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02009988 int id_STNC = -1;
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009989# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009990 int hlcnt;
9991# endif
9992#endif
9993 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9994
9995 need_highlight_changed = FALSE;
9996
9997 /*
9998 * Clear all attributes.
9999 */
10000 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
10001 highlight_attr[hlf] = 0;
10002
10003 /*
10004 * First set all attributes to their default value.
10005 * Then use the attributes from the 'highlight' option.
10006 */
10007 for (i = 0; i < 2; ++i)
10008 {
10009 if (i)
10010 p = p_hl;
10011 else
10012 p = get_highlight_default();
10013 if (p == NULL) /* just in case */
10014 continue;
10015
10016 while (*p)
10017 {
10018 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
10019 if (hl_flags[hlf] == *p)
10020 break;
10021 ++p;
10022 if (hlf == (int)HLF_COUNT || *p == NUL)
10023 return FAIL;
10024
10025 /*
10026 * Allow several hl_flags to be combined, like "bu" for
10027 * bold-underlined.
10028 */
10029 attr = 0;
10030 for ( ; *p && *p != ','; ++p) /* parse upto comma */
10031 {
Bram Moolenaar1c465442017-03-12 20:10:05 +010010032 if (VIM_ISWHITE(*p)) /* ignore white space */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010033 continue;
10034
10035 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
10036 return FAIL;
10037
10038 switch (*p)
10039 {
10040 case 'b': attr |= HL_BOLD;
10041 break;
10042 case 'i': attr |= HL_ITALIC;
10043 break;
10044 case '-':
10045 case 'n': /* no highlighting */
10046 break;
10047 case 'r': attr |= HL_INVERSE;
10048 break;
10049 case 's': attr |= HL_STANDOUT;
10050 break;
10051 case 'u': attr |= HL_UNDERLINE;
10052 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +000010053 case 'c': attr |= HL_UNDERCURL;
10054 break;
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +020010055 case 't': attr |= HL_STRIKETHROUGH;
10056 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010057 case ':': ++p; /* highlight group name */
10058 if (attr || *p == NUL) /* no combinations */
10059 return FAIL;
10060 end = vim_strchr(p, ',');
10061 if (end == NULL)
10062 end = p + STRLEN(p);
10063 id = syn_check_group(p, (int)(end - p));
10064 if (id == 0)
10065 return FAIL;
10066 attr = syn_id2attr(id);
10067 p = end - 1;
10068#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
10069 if (hlf == (int)HLF_SNC)
10070 id_SNC = syn_get_final_id(id);
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010071# ifdef FEAT_TERMINAL
10072 else if (hlf == (int)HLF_ST)
10073 id_ST = syn_get_final_id(id);
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010074 else if (hlf == (int)HLF_STNC)
10075 id_STNC = syn_get_final_id(id);
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010076# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010077 else if (hlf == (int)HLF_S)
10078 id_S = syn_get_final_id(id);
10079#endif
10080 break;
10081 default: return FAIL;
10082 }
10083 }
10084 highlight_attr[hlf] = attr;
10085
10086 p = skip_to_option_part(p); /* skip comma and spaces */
10087 }
10088 }
10089
10090#ifdef USER_HIGHLIGHT
10091 /* Setup the user highlights
10092 *
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010093 * Temporarily utilize 28 more hl entries:
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010094 * 9 for User1-User9 combined with StatusLineNC
10095 * 9 for User1-User9 combined with StatusLineTerm
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010096 * 9 for User1-User9 combined with StatusLineTermNC
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010097 * 1 for StatusLine default
10098 * Have to be in there simultaneously in case of table overflows in
10099 * get_attr_entry()
Bram Moolenaar071d4272004-06-13 20:20:40 +000010100 */
10101# ifdef FEAT_STL_OPT
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010102 if (ga_grow(&highlight_ga, 28) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010103 return FAIL;
10104 hlcnt = highlight_ga.ga_len;
Bram Moolenaard6a7b3e2017-08-19 21:35:35 +020010105 if (id_S == -1)
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010106 {
10107 /* Make sure id_S is always valid to simplify code below. Use the last
10108 * entry. */
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010109 vim_memset(&HL_TABLE()[hlcnt + 27], 0, sizeof(struct hl_group));
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010110 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
10111 id_S = hlcnt + 19;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010112 }
10113# endif
10114 for (i = 0; i < 9; i++)
10115 {
10116 sprintf((char *)userhl, "User%d", i + 1);
10117 id = syn_name2id(userhl);
10118 if (id == 0)
10119 {
10120 highlight_user[i] = 0;
10121# ifdef FEAT_STL_OPT
10122 highlight_stlnc[i] = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010123# ifdef FEAT_TERMINAL
10124 highlight_stlterm[i] = 0;
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010125 highlight_stltermnc[i] = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010126# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010127# endif
10128 }
10129 else
10130 {
Bram Moolenaar071d4272004-06-13 20:20:40 +000010131 highlight_user[i] = syn_id2attr(id);
10132# ifdef FEAT_STL_OPT
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010133 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
10134 HLF_SNC, highlight_stlnc);
10135# ifdef FEAT_TERMINAL
10136 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
10137 HLF_ST, highlight_stlterm);
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010138 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
10139 HLF_STNC, highlight_stltermnc);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010140# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010141# endif
10142 }
10143 }
10144# ifdef FEAT_STL_OPT
10145 highlight_ga.ga_len = hlcnt;
10146# endif
10147
10148#endif /* USER_HIGHLIGHT */
10149
10150 return OK;
10151}
10152
Bram Moolenaar4f688582007-07-24 12:34:30 +000010153#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010154
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010010155static void highlight_list(void);
10156static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010157
10158/*
10159 * Handle command line completion for :highlight command.
10160 */
10161 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010162set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010163{
10164 char_u *p;
10165
10166 /* Default: expand group names */
10167 xp->xp_context = EXPAND_HIGHLIGHT;
10168 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +000010169 include_link = 2;
10170 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010171
10172 /* (part of) subcommand already typed */
10173 if (*arg != NUL)
10174 {
10175 p = skiptowhite(arg);
10176 if (*p != NUL) /* past "default" or group name */
10177 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010178 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010179 if (STRNCMP("default", arg, p - arg) == 0)
10180 {
10181 arg = skipwhite(p);
10182 xp->xp_pattern = arg;
10183 p = skiptowhite(arg);
10184 }
10185 if (*p != NUL) /* past group name */
10186 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010187 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010188 if (arg[1] == 'i' && arg[0] == 'N')
10189 highlight_list();
10190 if (STRNCMP("link", arg, p - arg) == 0
10191 || STRNCMP("clear", arg, p - arg) == 0)
10192 {
10193 xp->xp_pattern = skipwhite(p);
10194 p = skiptowhite(xp->xp_pattern);
10195 if (*p != NUL) /* past first group name */
10196 {
10197 xp->xp_pattern = skipwhite(p);
10198 p = skiptowhite(xp->xp_pattern);
10199 }
10200 }
10201 if (*p != NUL) /* past group name(s) */
10202 xp->xp_context = EXPAND_NOTHING;
10203 }
10204 }
10205 }
10206}
10207
10208/*
10209 * List highlighting matches in a nice way.
10210 */
10211 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010212highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010213{
10214 int i;
10215
10216 for (i = 10; --i >= 0; )
Bram Moolenaar8820b482017-03-16 17:23:31 +010010217 highlight_list_two(i, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +000010218 for (i = 40; --i >= 0; )
10219 highlight_list_two(99, 0);
10220}
10221
10222 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010223highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010224{
Bram Moolenaar112f3182012-06-01 13:18:53 +020010225 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010226 msg_clr_eos();
10227 out_flush();
10228 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
10229}
10230
10231#endif /* FEAT_CMDL_COMPL */
10232
10233#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
10234 || defined(FEAT_SIGNS) || defined(PROTO)
10235/*
10236 * Function given to ExpandGeneric() to obtain the list of group names.
Bram Moolenaar071d4272004-06-13 20:20:40 +000010237 */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010238 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010239get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010240{
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010241 return get_highlight_name_ext(xp, idx, TRUE);
10242}
10243
10244/*
10245 * Obtain a highlight group name.
10246 * When "skip_cleared" is TRUE don't return a cleared entry.
10247 */
10248 char_u *
10249get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
10250{
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010251 if (idx < 0)
10252 return NULL;
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010253
10254 /* Items are never removed from the table, skip the ones that were
10255 * cleared. */
10256 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
10257 return (char_u *)"";
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010258
Bram Moolenaar071d4272004-06-13 20:20:40 +000010259#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000010260 if (idx == highlight_ga.ga_len && include_none != 0)
10261 return (char_u *)"none";
10262 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010263 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +000010264 if (idx == highlight_ga.ga_len + include_none + include_default
10265 && include_link != 0)
10266 return (char_u *)"link";
10267 if (idx == highlight_ga.ga_len + include_none + include_default + 1
10268 && include_link != 0)
10269 return (char_u *)"clear";
10270#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +010010271 if (idx >= highlight_ga.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010272 return NULL;
10273 return HL_TABLE()[idx].sg_name;
10274}
10275#endif
10276
Bram Moolenaar4f688582007-07-24 12:34:30 +000010277#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010278/*
10279 * Free all the highlight group fonts.
10280 * Used when quitting for systems which need it.
10281 */
10282 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010283free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010284{
10285 int idx;
10286
10287 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
10288 {
10289 gui_mch_free_font(HL_TABLE()[idx].sg_font);
10290 HL_TABLE()[idx].sg_font = NOFONT;
10291# ifdef FEAT_XFONTSET
10292 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
10293 HL_TABLE()[idx].sg_fontset = NOFONTSET;
10294# endif
10295 }
10296
10297 gui_mch_free_font(gui.norm_font);
10298# ifdef FEAT_XFONTSET
10299 gui_mch_free_fontset(gui.fontset);
10300# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +020010301# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +000010302 gui_mch_free_font(gui.bold_font);
10303 gui_mch_free_font(gui.ital_font);
10304 gui_mch_free_font(gui.boldital_font);
10305# endif
10306}
10307#endif
10308
10309/**************************************
10310 * End of Highlighting stuff *
10311 **************************************/